package javaapplicationir_pointer;

import com.fazecast.jSerialComm.SerialPort;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.io.InputStream;
import javax.swing.DefaultComboBoxModel;

public class jFrameAPI extends javax.swing.JFrame {
    
    private int ScreenHeight = 600, ScreenWidth = 800, WaitPlease = 1, Accelerator = 10; 
    private int CounterA, CounterM, CounterE, CounterB, CounterN, CounterF, CounterC, CounterO, CounterG, CounterS;
    private char Data = ' ';
    private SerialPort comPort;
    private Thread hiloLectura;
    private boolean conectado = false;
    private Robot robotRaton;

    private ComponenteLED ledHardware;
    private javax.swing.Timer temporizadorLED;
    private int contadorDestellos = 0;

    private javax.swing.JButton jButtonCOMConnect;
    private javax.swing.JComboBox<String> jComboBoxScreenResolution;
    private javax.swing.JComboBox<String> jComboBoxSelectPort;
    private javax.swing.JSlider jSliderPointerSpeed;
    private javax.swing.JTextField jTextField1;
    private java.awt.Panel panelScreenSim;
    private java.awt.TextField textFieldIRInput;

    public jFrameAPI() {
        initComponents();
        try { robotRaton = new Robot(); } catch (Exception e) {}
        configuracionSistema();
    }

    private void configuracionSistema() {
        String[] misPuertosFijos = {"COM1", "COM2", "COM3", "COM4", "COM32"};
        jComboBoxSelectPort.setModel(new DefaultComboBoxModel<>(misPuertosFijos));
        String[] resoluciones = {"800x600", "1024x768", "1920x1080"};
        jComboBoxScreenResolution.setModel(new DefaultComboBoxModel<>(resoluciones));
        
        jSliderPointerSpeed.setMinimum(1); jSliderPointerSpeed.setMaximum(30); jSliderPointerSpeed.setValue(Accelerator);
        jTextField1.setText(String.valueOf(Accelerator));
        
        jSliderPointerSpeed.addChangeListener(e -> {
            Accelerator = jSliderPointerSpeed.getValue();
            jTextField1.setText(String.valueOf(Accelerator));
        });
        jButtonCOMConnect.addActionListener(e -> {
            if (!conectado) abrirPuertoCOM(); else cerrarPuertoCOM();
        });
        jComboBoxScreenResolution.addActionListener(e -> actualizarEstructuraSimulador());
        actualizarEstructuraSimulador();
    }

    private void abrirPuertoCOM() {
        if (jComboBoxSelectPort.getSelectedItem() == null) return;
        String puerto = jComboBoxSelectPort.getSelectedItem().toString();
        comPort = SerialPort.getCommPort(puerto);
        comPort.setBaudRate(9600); 
        if (comPort.openPort()) {
            conectado = true; jButtonCOMConnect.setText("Close"); jComboBoxSelectPort.setEnabled(false);
            hiloLectura = new Thread(() -> {
                try (InputStream in = comPort.getInputStream()) {
                    while (conectado) {
                        if (in.available() > 0) {
                            Data = (char) in.read();
                            dispararDestelloLED(); 
                            java.awt.EventQueue.invokeLater(() -> textFieldIRInput.setText(String.valueOf(Data)));
                            analizarProtocoloIR(Data);
                        }
                        Thread.sleep(1); 
                    }
                } catch (Exception ex) { cerrarPuertoCOM(); }
            });
            hiloLectura.start();
        }
    }

    private void cerrarPuertoCOM() {
        conectado = false;
        if (comPort != null && comPort.isOpen()) comPort.closePort();
        jButtonCOMConnect.setText("Open"); jComboBoxSelectPort.setEnabled(true);
    }

    private void dispararDestelloLED() {
        if (temporizadorLED != null && temporizadorLED.isRunning()) { contadorDestellos = 0; return; }
        contadorDestellos = 0;
        temporizadorLED = new javax.swing.Timer(60, e -> {
            if (contadorDestellos < 6) {
                if (contadorDestellos % 2 == 0) ledHardware.encender(Color.GREEN); else ledHardware.apagar();
                contadorDestellos++;
            } else { ledHardware.apagar(); ((javax.swing.Timer)e.getSource()).stop(); }
        });
        temporizadorLED.start();
    }

    private void recibirByteFrenado() {
        try { if (comPort != null && comPort.bytesAvailable() > 0) { byte[] buffer = new byte[1]; comPort.readBytes(buffer, 1); Data = (char) buffer[0]; } } catch(Exception e) {}
    }
    
    private void ResetCounters() {
        CounterA = 0; CounterM = 0; CounterE = 0; CounterB = 0; CounterF = 0; CounterC = 0; CounterO = 0; CounterG = 0; CounterS = 0;
    }

    private void MoveLeft() { Point coord = MouseInfo.getPointerInfo().getLocation(); robotRaton.mouseMove(coord.x - 10, coord.y); }
    
    private void MoveLeftFast() {
        do {
            Point coord = MouseInfo.getPointerInfo().getLocation();
            if (coord.x > 3) { robotRaton.mouseMove(coord.x - Accelerator, coord.y); try { Thread.sleep(WaitPlease); } catch(Exception e){} }
            recibirByteFrenado();
        } while (Data != 'A' && Data != 'M' && Data != 'B' && Data != 'E' && Data != 'N' && Data != 'F' && Data != 'C' && Data != 'O' && Data != 'G' && Data != '@');
        ResetCounters();
    }

    private void MoveRight() { Point coord = MouseInfo.getPointerInfo().getLocation(); robotRaton.mouseMove(coord.x + 10, coord.y); }
    
    private void MoveRightFast() {
        do {
            Point coord = MouseInfo.getPointerInfo().getLocation();
            if (coord.x < ScreenWidth - 3) { robotRaton.mouseMove(coord.x + Accelerator, coord.y); try { Thread.sleep(WaitPlease); } catch(Exception e){} }
            recibirByteFrenado();
        } while (Data != 'A' && Data != 'M' && Data != 'B' && Data != 'E' && Data != 'N' && Data != 'F' && Data != 'C' && Data != 'O' && Data != 'G' && Data != '@');
        ResetCounters();
    }

    private void HoldUp() { robotRaton.mousePress(InputEvent.BUTTON1_DOWN_MASK); }
    private void Modifier() { robotRaton.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); }

    private void analizarProtocoloIR(char caracter) {
        switch(caracter) {
            case 'A': CounterA++; if (CounterA == 1) MoveLeft(); else if (CounterA >= 2) MoveLeftFast(); break;
            case 'B': CounterB++; if (CounterB == 1) MoveRight(); else if (CounterB >= 2) MoveRightFast(); break;
            case 'U': HoldUp(); break; case 'D': Modifier(); break; case '@': ResetCounters(); break;
        }
    }

    private void actualizarEstructuraSimulador() {
        int index = jComboBoxScreenResolution.getSelectedIndex();
        if (index == 0) { ScreenWidth = 800; ScreenHeight = 600; }
        else if (index == 1) { ScreenWidth = 1024; ScreenHeight = 768; }
        else if (index == 2) { ScreenWidth = 1920; ScreenHeight = 1080; }
        
        // Escalamos un poco más para que la simulación sea compacta
        panelScreenSim.setPreferredSize(new Dimension(ScreenWidth / 6, ScreenHeight / 6));
        panelScreenSim.setSize(new Dimension(ScreenWidth / 6, ScreenHeight / 6));
        panelScreenSim.setBackground(Color.DARK_GRAY);
        
        // ¡El gran truco! Fuerza a la ventana a recalcular y adaptarse al nuevo tamaño del panel
        this.getContentPane().validate();
        this.getContentPane().repaint();
        this.pack(); 
    }

    @SuppressWarnings("unchecked")
    private void initComponents() {
        jButtonCOMConnect = new javax.swing.JButton("Connect");
        jComboBoxSelectPort = new javax.swing.JComboBox<>();
        jSliderPointerSpeed = new javax.swing.JSlider();
        jTextField1 = new javax.swing.JTextField(3); // Cuadro más compacto
        jComboBoxScreenResolution = new javax.swing.JComboBox<>();
        panelScreenSim = new java.awt.Panel();
        textFieldIRInput = new java.awt.TextField(3); // Cuadro más compacto
        ledHardware = new ComponenteLED();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Decodificador Infrarrojos - JavaApplicationIR_Pointer");
        
        this.getContentPane().setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        c.insets = new Insets(4, 4, 4, 4); // ¡Márgenes ultra compactos reducidos a 4px!
        c.fill = GridBagConstraints.HORIZONTAL;

        // FILA 1: CONEXIÓN SERIE Y LED
        c.gridx = 0; c.gridy = 0; this.getContentPane().add(jComboBoxSelectPort, c);
        c.gridx = 1; c.gridy = 0; this.getContentPane().add(jButtonCOMConnect, c);
        c.gridx = 2; c.gridy = 0; c.fill = GridBagConstraints.NONE; this.getContentPane().add(ledHardware, c);

        // FILA 2: CONTROL DE VELOCIDAD
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 0; c.gridy = 1; c.gridwidth = 2; this.getContentPane().add(jSliderPointerSpeed, c);
        c.gridx = 2; c.gridy = 1; c.gridwidth = 1; this.getContentPane().add(jTextField1, c);

        // FILA 3: RESOLUCIÓN Y CARÁCTER IR RECIBIDO
        c.gridx = 0; c.gridy = 2; this.getContentPane().add(jComboBoxScreenResolution, c);
        c.gridx = 1; c.gridy = 2; this.getContentPane().add(textFieldIRInput, c);

        // FILA 3 (DERECHA): SIMULACIÓN DE PANTALLA ELÁSTICA
        c.gridx = 2; c.gridy = 2; c.fill = GridBagConstraints.BOTH;
        this.getContentPane().add(panelScreenSim, c);

        this.pack();
        this.setLocationRelativeTo(null); // Centra de nuevo la app tras cambiar de tamaño
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(() -> {
            new jFrameAPI().setVisible(true);
        });
    }

    class ComponenteLED extends javax.swing.JPanel {
        private Color colorActual = Color.DARK_GRAY;
        public ComponenteLED() { 
            setPreferredSize(new Dimension(24, 24)); 
            setSize(new Dimension(24, 24)); 
            setOpaque(false); 
        }
        public void encender(Color c) { colorActual = c; repaint(); }
        public void apagar() { colorActual = Color.DARK_GRAY; repaint(); }
        
        @Override
        protected void paintComponent(java.awt.Graphics g) {
            super.paintComponent(g); 
            java.awt.Graphics2D g2 = (java.awt.Graphics2D) g;
            g2.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
            g2.setColor(colorActual); 
            g2.fillOval(2, 2, 20, 20);
            if (colorActual != Color.DARK_GRAY) { 
                g2.setColor(new Color(255, 255, 255, 180)); 
                g2.fillOval(6, 6, 6, 6); 
            }
            g2.setColor(Color.BLACK); 
            g2.setStroke(new java.awt.BasicStroke(1.5f)); 
            g2.drawOval(2, 2, 20, 20);
        }
    }
}
