package de.serosystems.grx.example;

import de.serosystems.proto.v3.grx.receiverd.ReceiverDProto;
import de.serosystems.proto.v3.grx.receiverd.ReceiverdGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

import java.util.Iterator;

/**
 * Example: Subscribe to Mode S downlink frames in a blocking (synchronous) fashion.
 *
 * <p>This example connects to the Receiverd service and opens a server-streaming RPC
 * to receive Mode S downlink frames for DF 11 (All-Call Reply) and DF 17 (ADS-B).
 * It collects 10 frames, prints hex payload and signal level, then exits.</p>
 *
 * <p>Usage: {@code java BlockingModeSDownlink [host]}</p>
 * <p>Default host: YOUR_GRX_IP</p>
 */
public class BlockingModeSDownlink {

	/** Default IP address of the GRX receiver. */
	private static final String DEFAULT_HOST = "YOUR_GRX_IP";

	/** Receiverd gRPC port. */
	private static final int RECEIVERD_PORT = 5303;

	/** Number of frames to collect before exiting. */
	private static final int FRAME_COUNT = 10;

	public static void main(String[] args) {
		// Use the first CLI argument as hostname, or fall back to the default
		String host = args.length > 0 ? args[0] : DEFAULT_HOST;

		// Create a plaintext gRPC channel to the Receiverd service
		ManagedChannel channel = ManagedChannelBuilder.forAddress(host, RECEIVERD_PORT)
				.usePlaintext()
				.build();

		try {
			// Create a blocking (synchronous) stub for the Receiverd service
			ReceiverdGrpc.ReceiverdBlockingStub stub = ReceiverdGrpc.newBlockingStub(channel);

			// Build the subscription request for DF 11 and DF 17
			ReceiverDProto.GetModeSDownlinkFramesRequest request =
					ReceiverDProto.GetModeSDownlinkFramesRequest.newBuilder()
							.addDownlinkFormats(11)  // All-Call Reply
							.addDownlinkFormats(17)  // Extended Squitter (ADS-B)
							.build();

			// Open the server-streaming RPC; this returns a blocking iterator
			Iterator<ReceiverDProto.ModeSDownlinkFrameWithStreamInfo> frameIterator =
					stub.getModeSDownlinkFrames(request);

			// Collect FRAME_COUNT frames and print details for each
			int count = 0;
			while (frameIterator.hasNext() && count < FRAME_COUNT) {
				ReceiverDProto.ModeSDownlinkFrameWithStreamInfo msg = frameIterator.next();
				ReceiverDProto.ModeSDownlinkFrame frame = msg.getFrame();
				count++;

				// Convert payload bytes to hex string
				StringBuilder hex = new StringBuilder();
				for (byte b : frame.getPayload().toByteArray()) {
					hex.append(String.format("%02x", b));
				}

				// Extract the downlink format from the first byte (top 5 bits)
				int df = (frame.getPayload().byteAt(0) & 0xFF) >> 3;

				System.out.printf("Frame %2d: DF=%2d  t=%d  signal=%.1f dBm  noise=%.1f dBm  payload=%s%n",
						count, df, frame.getTimestamp(),
						frame.getLevelSignal(), frame.getLevelNoise(),
						hex);
			}

			System.out.printf("%nCollected %d frames.%n", count);

		} catch (Exception e) {
			System.err.println("Error during Mode S subscription: " + e.getMessage());
			e.printStackTrace();
		} finally {
			// Always shut down the channel to release resources
			channel.shutdownNow();
		}
	}
}
