package de.serosystems.grx.example;

import de.serosystems.proto.v3.grx.spectrumd.SpectrumDProto;
import de.serosystems.proto.v3.grx.spectrumd.SpectrumdGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;

/**
 * Example: Retrieve a waterfall spectrum JPEG from the Spectrumd service and display it.
 *
 * <p>This example connects to the Spectrumd service, first queries the aggregated FFT
 * properties to determine the appropriate number of lines for a 10-second waterfall,
 * then requests a JPEG waterfall image and displays it in a Swing JFrame window.</p>
 *
 * <p>Usage: {@code java SpecShot [host]}</p>
 * <p>Default host: YOUR_GRX_IP</p>
 */
public class SpecShot {

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

	/** Spectrumd gRPC port. */
	private static final int SPECTRUMD_PORT = 5306;

	/** Duration of the waterfall capture [seconds]. */
	private static final float CAPTURE_DURATION_SECONDS = 10.0f;

	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 Spectrumd service
		ManagedChannel channel = ManagedChannelBuilder.forAddress(host, SPECTRUMD_PORT)
				.usePlaintext()
				.build();

		try {
			// Create a blocking stub for the Spectrumd service
			SpectrumdGrpc.SpectrumdBlockingStub stub = SpectrumdGrpc.newBlockingStub(channel);

			// Step 1: Query the aggregated FFT properties to compute waterfall parameters.
			// We use an empty request (default rx_channel_index = 0).
			SpectrumDProto.AggregatedFFTProperties props = stub.getAggregatedFFTProperties(
					SpectrumDProto.AggregatedFFTRequest.newBuilder().build());

			System.out.printf("FFT Properties: center=%d Hz, sample_rate=%d Hz, fft_size=%d, aggregation=%d%n",
					props.getCenterFrequency(), props.getSampleRate(),
					props.getFftSize(), props.getAggregationFactor());

			// Compute the duration of a single aggregated FFT line
			float durationPerLine = props.getAggregationFactor() * props.getFftSize()
					/ (float) props.getSampleRate();

			// Number of lines needed to cover the desired capture duration
			int numLines = (int) Math.ceil(CAPTURE_DURATION_SECONDS / durationPerLine);

			System.out.printf("Will collect data for %.2f seconds (%d lines).%n",
					numLines * durationPerLine, numLines);

			// Step 2: Request the waterfall JPEG image
			SpectrumDProto.GetWaterfallJPEGRequest request =
					SpectrumDProto.GetWaterfallJPEGRequest.newBuilder()
							.setNumLines(numLines)
							.setMinLevel(-180)    // lower color scale limit [dBm]
							.setMaxLevel(-90)     // upper color scale limit [dBm]
							.setJpegQuality(90)   // JPEG quality [%]
							.build();

			// This call blocks until enough FFT data has been collected
			SpectrumDProto.WaterfallJPEGImage img = stub.getWaterfallJPEG(request);

			System.out.printf("Received waterfall JPEG: %d bytes, timestamp=%d%n",
					img.getImage().size(), img.getTimestamp());

			// Step 3: Decode the JPEG and display it in a Swing window
			BufferedImage buffered;
			try {
				buffered = ImageIO.read(new ByteArrayInputStream(img.getImage().toByteArray()));
			} catch (IOException e) {
				System.err.println("Failed to decode JPEG image: " + e.getMessage());
				return;
			}

			ImageIcon icon = new ImageIcon(buffered);
			JFrame frame = new JFrame("GRX Waterfall Spectrum");
			frame.setLayout(new FlowLayout());
			frame.setSize(icon.getIconWidth() + 20, icon.getIconHeight() + 50);
			JLabel label = new JLabel(icon);
			frame.add(label);
			frame.setVisible(true);
			frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

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