import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

/**
 * TCP Client that sends 1 byte and receives 4 bytes from a remote server
 */
public class TCPClientWiFi_Tx1Rx4 {
    
    // Default connection parameters
    private static final String DEFAULT_HOST = "localhost";
    private static final int DEFAULT_PORT = 9090;
    private static final int TIMEOUT_MS = 20000;  // 9 seconds timeout
    
    // Data to send
    private static final byte DATA_TO_SEND = 0x42;  // 'B' character (66 in decimal)
    
    private String host;
    private int port;
    private int timeout;
    
    /**
     * Constructor with default values (localhost:8080, 5s timeout)
     */
    public TCPClientWiFi_Tx1Rx4() {
        this(DEFAULT_HOST, DEFAULT_PORT, TIMEOUT_MS);
    }
    
    /**
     * Constructor with custom host and port
     * @param host Server hostname or IP address
     * @param port Server port number
     */
    public TCPClientWiFi_Tx1Rx4(String host, int port) {
        this(host, port, TIMEOUT_MS);
    }
    
    /**
     * Constructor with full customization
     * @param host Server hostname or IP address
     * @param port Server port number
     * @param timeout Socket timeout in milliseconds
     */
    public TCPClientWiFi_Tx1Rx4(String host, int port, int timeout) {
        this.host = host;
        this.port = port;
        this.timeout = timeout;
    }
    
    /**
     * Connects to server, sends 1 byte, receives 4 bytes
     * @return The 4-byte response as a byte array
     * @throws IOException If connection or communication fails
     */
    public byte[] sendAndReceive() throws IOException {
        Socket socket = null;
        
        try {
            // Create socket and connect to server
            System.out.printf("Connecting to %s:%d...%n", host, port);
            socket = new Socket(host, port);
            socket.setSoTimeout(timeout);
            System.out.println("Connected successfully!");
            
            // Get input and output streams
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            
            // Send 1 byte to server
            System.out.printf("Sending 1 byte: 0x%02X (%d)%n", DATA_TO_SEND, DATA_TO_SEND);
            outputStream.write(DATA_TO_SEND);
            outputStream.flush();
            System.out.println("Byte sent successfully!");
            
            // Receive 4 bytes from server
            byte[] responseBuffer = new byte[4];
            int totalBytesRead = 0;
            int bytesRead;
            
            System.out.println("Waiting for response (4 bytes)...");
            
            // Keep reading until we have 4 bytes or timeout occurs
            while (totalBytesRead < 4) {
                bytesRead = inputStream.read(responseBuffer, totalBytesRead, 
                                             4 - totalBytesRead);
                if (bytesRead == -1) {
                    throw new IOException("Server closed connection before sending 4 bytes");
                }
                totalBytesRead += bytesRead;
                System.out.printf("Received %d byte(s) (total: %d/4)%n", 
                                 bytesRead, totalBytesRead);
            }
            
            System.out.println("Successfully received all 4 bytes!");
            
            // Print received data in hex format
            System.out.print("Received bytes: ");
            for (byte b : responseBuffer) {
                System.out.printf("0x%02X ", b);
            }
            System.out.println();
            
            return responseBuffer;
            
        } catch (UnknownHostException e) {
            throw new IOException("Unknown host: " + host, e);
        } catch (SocketTimeoutException e) {
            throw new IOException("Timeout: Server did not respond within " + timeout + "ms", e);
        } finally {
            // Close the socket
            if (socket != null && !socket.isClosed()) {
                try {
                    socket.close();
                    System.out.println("Connection closed.");
                } catch (IOException e) {
                    System.err.println("Error closing socket: " + e.getMessage());
                }
            }
        }
    }
    
    /**
     * Alternative method that allows custom byte to send
     * @param dataToSend Single byte to send to server
     * @return The 4-byte response as a byte array
     * @throws IOException If connection or communication fails
     */
    public byte[] sendAndReceive(byte dataToSend) throws IOException {
        Socket socket = null;
        
        try {
            socket = new Socket(host, port);
            socket.setSoTimeout(timeout);
            
            OutputStream outputStream = socket.getOutputStream();
            InputStream inputStream = socket.getInputStream();
            
            // Send custom byte
            outputStream.write(dataToSend);
            outputStream.flush();
            
            // Receive 4 bytes
            byte[] responseBuffer = new byte[4];
            int totalBytesRead = 0;
            int bytesRead;
            
            while (totalBytesRead < 4) {
                bytesRead = inputStream.read(responseBuffer, totalBytesRead, 
                                             4 - totalBytesRead);
                if (bytesRead == -1) {
                    throw new IOException("Server closed connection");
                }
                totalBytesRead += bytesRead;
            }
            
            return responseBuffer;
            
        } finally {
            if (socket != null && !socket.isClosed()) {
                socket.close();
            }
        }
    }
    
    /**
     * Main method for direct execution
     */
    public static void main(String[] args) {
        // Parse command line arguments
        String host = DEFAULT_HOST;
        int port = DEFAULT_PORT;
        
        if (args.length > 0) {
            host = args[0];
        }
        if (args.length > 1) {
            try {
                port = Integer.parseInt(args[1]);
            } catch (NumberFormatException e) {
                System.err.println("Invalid port number. Using default port: " + DEFAULT_PORT);
                port = DEFAULT_PORT;
            }
        }
        
        // Create client and connect
        var client = new TCPClientWiFi_Tx1Rx4(host, port);
        
        try {
            byte[] response = client.sendAndReceive();
            
            // Additional processing of the response
            System.out.println("\n=== Response Analysis ===");
            System.out.println("Raw bytes: " + java.util.Arrays.toString(response));
            
            // Interpret as integer (big-endian)
            int intValue = ((response[0] & 0xFF) << 24) |
                          ((response[1] & 0xFF) << 16) |
                          ((response[2] & 0xFF) << 8) |
                          (response[3] & 0xFF);
            System.out.println("As 32-bit integer: " + intValue);
            
            // Try to interpret as string if printable
            String stringValue = new String(response);
            System.out.println("As string: " + stringValue);
            
        } catch (IOException e) {
            System.err.println("Error: " + e.getMessage());
            e.printStackTrace();
            System.exit(1);
        }
    }
}