package hh.dejsem.hack;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import hh.dejsem.K;
import hh.dejsem.net.NetCrypt;
import hh.dejsem.net.NetUDP;
import hh.lib.D;

public class HackDgrams extends Thread {
	final static int BCAST = 12;
	final static int SEND = 13;
	final static int REC = 14;
	final static int CLOSE = 15;
	final static int CHECK = 16;
	final static int ECHO = 17;
	final static String NULL_HOST = "0.0.0.0";
	static volatile Set<DatagramSocket> sockets = new HashSet<>(100);
	static volatile NetCrypt c = null;

	D d;
	NetUDP netUDP;
	int dgramAction;
	int port = K.UDP_PORT;
	DatagramSocket socket;
	String msg = "";
	String host = NULL_HOST;
	InetAddress ia = null;

	HackDgrams(D d, int dgramAction, String msg, InetAddress ia) {
		this(d, dgramAction, msg);
		this.ia = ia;
	}

	HackDgrams(D d, int dgramAction, String msg, String host) throws UnknownHostException {
		this(d, dgramAction, msg);
		this.host = host;
		ia = InetAddress.getByName(host);
	}

	HackDgrams(D d, int dgramAction, String msg) {
		this(d, dgramAction);
		this.msg = msg;
	}

	HackDgrams(D d, int dgramAction) {
		this(d);
		this.dgramAction = dgramAction;
	}

	HackDgrams(D d) {
		this.d = d.klon(getClass().getSimpleName() + "." + hashCode());
		synchronized(this) { if(c == null) c = new NetCrypt(d,null, "zatmívačka", 0); }
	}

	@Override
	public void run() {
//		sendToUI( 1, "zkouška spojení");
//		d.appPrefix("" + hashCode());
		d.l("+++ action=" + dgramAction);
		if(dgramAction == CHECK) encDec();
		//else if(dgramAction == REC) { netUDP = new NetUDP(d); netUDP.keepReceivingPeerIp(); } // zakryto kvůli neošetřené Exc
		else if(dgramAction == SEND) sendDgram("//" + msg);
		else if(dgramAction == BCAST) sendBroadcast("//" + msg);
		else if(dgramAction == ECHO) echoDgrams();
		else if(dgramAction == CLOSE) close();
	}

	DatagramPacket allocRecDgram() throws Exception {
		socket = new DatagramSocket(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), K.UDP_PORT));
//		sockets.add(socket);
		socket.setBroadcast(true);
		byte[] recvBuf = new byte[15000];
		DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
		d.l("receiving UDP allocated");
		return packet;
	}

	String receiveUDPData(DatagramPacket packet) throws Exception {
		socket.receive(packet);
		int len = packet.getLength();
		byte[] enc = Arrays.copyOf(packet.getData(), len);
		String data = "";
		try { data = c.decrypt(enc); }
		catch(Exception e) { return null; }
		if(!data.startsWith("//")) return null;
		data = data.replaceFirst("^//", "");
		return data;
	}

	void echoDgrams() {
		DatagramPacket packet = null;
		try {
			packet = allocRecDgram();
			d.l("receiving encrypted UDP packets...");
			while(netUDP.go) {
				String data = receiveUDPData(packet);
				ia = packet.getAddress();
				String ip = "unknown";
				if(ia != null) ip = ia.getHostAddress();
				if(data == null) continue;
				String s = String.format("encrypted UDP dgram [%s] received from %s", data, ip);
				d.l(s);

				if(ia != null && data.indexOf("echo") < 0) {
					d.l(String.format("sending echo to %s...", ip));
					try { Thread.sleep(1000, 0); } catch(InterruptedException e) {}
					msg = String.format("%s: echo: %s", NetUDP.localAddr.getHostAddress(), data);
					sendDgram("//" + msg);
				}
			}
		}
		catch(SocketException e) { d.l("socket closed"); }
		catch(Exception e) {
			d.l(String.format("datagram exc errno=%d, emsg=[%s]", d.errno(e), e.getMessage()));
			d.abendMsg("Oops", e);
		}
	}

	void sendBroadcast(String msg) {
		ia = NetUDP.broadcastAddr;
		sendDgram(msg);
	}

	void sendDgram(String data) {
		try {
			DatagramSocket socket = new DatagramSocket();
			socket.setBroadcast(true);
			byte[] sendData = c.encrypt(data);
			if(ia == null) ia = InetAddress.getByName(host);
			DatagramPacket packet = new DatagramPacket(sendData, sendData.length, ia, K.UDP_PORT);
			socket.send(packet);
			d.l(String.format("UDP dgram [%s] sent to %s", msg, ia.getHostAddress()));
		} catch (Exception e) { d.abendMsg("send broadcast UDP: ", e); }
	}

	void sendToUI(int what, String data) {
		try { d.l(data); }
		catch(Exception e) { d.abendMsg("messenger problem", e); }
	}

	void close() {
//		d.l("+++ closing, sockets num=" + sockets.size());
		/*for(DatagramSocket socket: sockets) {
			socket.close();
			sockets.remove(socket);
		}*/
		socket.close();
	}

	void encDec() { try { d.l(c.decrypt(c.encrypt("encrypt/decrypt test"))); } catch(Exception e) {} }
}
