package org.kne.cloud.network.srv6;

import java.io.IOException;
import java.lang.ref.Cleaner;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import org.kne.cloud.network.MultiProtocolSocketAddress;
import org.kne.cloud.network.congestion.NOCongestionAlgorithm;
import org.kne.cloud.network.congestion.SendPacketSlidingWindow;
import org.kne.opencl64.Releaser;

public class KLALBRoutingProtocolAPIClient {
	private KLALBRoutingProtocol routingProtocol;
	private SendPacketSlidingWindow<UUID, JsonDataPacket> window = new SendPacketSlidingWindow<UUID, JsonDataPacket>(
			new NOCongestionAlgorithm(3000000000L), 1024 * 1024);

	private static final Cleaner clr = Cleaner.create();

	private BiConsumer<SocketAddress, JsonDataPacket> rec = (addr, data) -> {
		KLALBRoutingProtocolJsonData dataobj = data.getDecodedData();
		InetSocketAddress addrs = (InetSocketAddress) addr;
		UUID ruid = dataobj.getUuid();
		JsonDataPacket relate = null;
		//System.out.println(ruid + " " + window.getSendmap());
		switch (dataobj.getType()) {
		case KLALBRoutingProtocolJsonData.NODE_INFO_FULL_RESP:
		case KLALBRoutingProtocolJsonData.NODE_INFO_TINY_RESP:

			if ((relate = window.ack(ruid)) != null) {
				List<?> connects = (List<?>) dataobj.getData();
				List<MultiProtocolSocketAddress> connectsm = new ArrayList<MultiProtocolSocketAddress>(
						connects == null ? 0 : connects.size());
				if (connects != null) {
					for (Object open : connects) {
						if (open instanceof MultiProtocolSocketAddress) {
							connectsm.add((MultiProtocolSocketAddress) open);
						} else {
							connectsm.add(new MultiProtocolSocketAddress((String) open));

						}
					}
				}
				// 组装节点信息（线路 + 设备名称 + 设备描述，精简模式下线路与描述为 null）
				KLALBNodeInformation info = new KLALBNodeInformation(connectsm, dataobj.getDeviceName(),
						dataobj.getDeviceDescription());
				((Consumer<KLALBNodeInformation>) relate.getUserCallback()).accept(info);
			}
			break;
		}

	};

	public KLALBRoutingProtocolAPIClient(KLALBRoutingProtocol routingProtocol) {
		this.routingProtocol = routingProtocol;
		routingProtocol.addReceiver(rec);
		this.releaser = new KLALBRoutingProtocolAPIClientReleaser(this.routingProtocol, rec, window);
		clr.register(this, releaser);
	}

	/**
	 * 精简查询：仅获取对端设备名称（开销最小，适用于未查看节点详情的场景）。
	 */
	public void requestNodeInfoTiny(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
			throws IOException {
		sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_TINY_REQ, addr, callback);
	}

	/**
	 * 完整查询：获取对端开放线路 + 设备名称 + 设备描述（查看节点信息时使用）。
	 */
	public void requestNodeInfoFull(SocketAddress addr, Consumer<KLALBNodeInformation> callback)
			throws IOException {
		sendNodeInfoRequest(KLALBRoutingProtocolJsonData.NODE_INFO_FULL_REQ, addr, callback);
	}

	private void sendNodeInfoRequest(String type, SocketAddress addr, Consumer<KLALBNodeInformation> callback)
			throws IOException {
		UUID suid = UUID.randomUUID();
		KLALBRoutingProtocolJsonData json = new KLALBRoutingProtocolJsonData(type, suid, null);
		JsonDataPacket packet = new JsonDataPacket(json);
		packet.setUserCallback(callback);
		window.put(suid, packet);
		routingProtocol.sendJsonPacketToAddress(packet, addr);

	}

	public KLALBRoutingProtocol getRoutingProtocol() {
		return routingProtocol;
	}

	private KLALBRoutingProtocolAPIClientReleaser releaser;

	public void close() {
		releaser.run();
	}

	public boolean isClosed() {
		return releaser.isReleased();
	}
}

class KLALBRoutingProtocolAPIClientReleaser extends Releaser<BiConsumer<SocketAddress, JsonDataPacket>> {

	private KLALBRoutingProtocol routingProtocol;
	private SendPacketSlidingWindow<UUID, JsonDataPacket> window;

	public KLALBRoutingProtocolAPIClientReleaser(KLALBRoutingProtocol routingProtocol,
			BiConsumer<SocketAddress, JsonDataPacket> resource, SendPacketSlidingWindow<UUID, JsonDataPacket> window) {
		super(resource);
		this.routingProtocol = routingProtocol;
		this.window = window;
	}

	@Override
	protected void release(BiConsumer<SocketAddress, JsonDataPacket> resource) {
		window.close();
		routingProtocol.removeReceiver(resource);
	}
}