import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;

/**
 * Panneau personnalisé pour dessiner les signaux (NRZ, Manchester, etc.).
 * @author REDJRADJ YACINE
 */
public class MyPanel extends JPanel {

    // Constantes graphiques
    private static final int PANEL_WIDTH = 800;
    private static final int PANEL_HEIGHT = 200;
    private static final int MARGIN = 50;
    private static final int VERTICAL_OFFSET = 30;
    
    // Couleurs
    private static final Color GRID_COLOR = Color.LIGHT_GRAY;
    private static final Color SIGNAL_COLOR = Color.BLACK;

    private String chaineBinaire = "";
    private String methode = "NRZ";

    public MyPanel() {
        super();
        setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
        setBackground(Color.WHITE);
    }

    public void setSignal(String chaineBinaire, String methode) {
        // Vérification stricte de l'entrée (REGEX)
        if (!chaineBinaire.matches("[01]*")) {
            throw new IllegalArgumentException("La chaîne binaire doit contenir uniquement des '0' et '1'.");
        }
        this.chaineBinaire = chaineBinaire;
        this.methode = methode;
        repaint(); // Demande à Swing de redessiner le composant
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;

        int width = getWidth();
        int height = getHeight();

        // Calcul dynamique de la largeur d'un bit
        int bitWidth = (width - 2 * MARGIN) / Math.max(chaineBinaire.length(), 1);

        // Positions verticales des niveaux de tension
        int yNV = height / 4;            // +nV
        int y0V = height / 2;            // 0V
        int yMinusNV = (3 * height) / 4; // -nV

        drawGrid(g2d, width, yNV, y0V, yMinusNV);

        // Choix de la méthode de tracé
        switch (methode) {
            case "NRZ":
                drawNRZ(g2d, chaineBinaire, bitWidth, y0V, yNV, yMinusNV);
                break;
            case "Manchester":
                drawManchester(g2d, chaineBinaire, bitWidth, y0V, yNV, yMinusNV);
                break;
            case "Manchester Différentiel":
                drawManchesterDifferential(g2d, chaineBinaire, bitWidth, y0V, yNV, yMinusNV);
                break;
            case "Miller":
                drawMiller(g2d, chaineBinaire, bitWidth, y0V, yNV, yMinusNV);
                break;
        }
    }

    private void drawGrid(Graphics2D g2d, int width, int yNV, int y0V, int yMinusNV) {
        float[] dashPattern = { 5, 5 };
        g2d.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10, dashPattern, 0));
        g2d.setColor(GRID_COLOR);

        // Lignes horizontales
        g2d.drawLine(MARGIN, yNV, width - MARGIN, yNV);
        g2d.drawLine(MARGIN, y0V, width - MARGIN, y0V);
        g2d.drawLine(MARGIN, yMinusNV, width - MARGIN, yMinusNV);

        // Lignes verticales (séparateurs de bits)
        int bitWidth = (width - 2 * MARGIN) / Math.max(chaineBinaire.length(), 1);
        for (int i = 0; i <= chaineBinaire.length(); i++) {
            int x = MARGIN + i * bitWidth;
            g2d.drawLine(x, yNV - VERTICAL_OFFSET, x, yMinusNV + VERTICAL_OFFSET);
        }

        // Labels
        g2d.setColor(Color.BLACK);
        g2d.setFont(new Font("Arial", Font.PLAIN, 12));
        g2d.drawString("+nV", 10, yNV);
        g2d.drawString("0V", 10, y0V);
        g2d.drawString("-nV", 10, yMinusNV);
    }

    // --- Méthodes de tracé des signaux ---

    private void drawNRZ(Graphics2D g2d, String chaine, int bitWidth, int y0V, int yNV, int yMinusNV) {
        g2d.setColor(SIGNAL_COLOR);
        g2d.setStroke(new BasicStroke(2.0f));
        int x = MARGIN;
        int currentY = chaine.isEmpty() ? y0V : (chaine.charAt(0) == '1' ? yNV : yMinusNV);

        for (int i = 0; i < chaine.length(); i++) {
            char bit = chaine.charAt(i);
            int nextY = bit == '1' ? yNV : yMinusNV;

            if (i > 0 && chaine.charAt(i - 1) != bit) {
                g2d.drawLine(x, currentY, x, nextY);
            }
            g2d.drawLine(x, nextY, x + bitWidth, nextY);
            currentY = nextY;
            x += bitWidth;
        }
    }

    private void drawManchester(Graphics2D g2d, String chaine, int bitWidth, int y0V, int yNV, int yMinusNV) {
        g2d.setColor(SIGNAL_COLOR);
        g2d.setStroke(new BasicStroke(2.0f));
        int x = MARGIN;
        int previousEndY = yNV; // Valeur arbitraire pour le début

        for (int i = 0; i < chaine.length(); i++) {
            char bit = chaine.charAt(i);
            int startY = bit == '1' ? yNV : yMinusNV;
            int endY = bit == '1' ? yMinusNV : yNV;

            // Transition entre bits si nécessaire
            if (i > 0 && previousEndY != startY) {
                g2d.drawLine(x, previousEndY, x, startY);
            }

            // Demi-bit 1
            g2d.drawLine(x, startY, x + bitWidth / 2, startY);
            // Transition milieu
            g2d.drawLine(x + bitWidth / 2, startY, x + bitWidth / 2, endY);
            // Demi-bit 2
            g2d.drawLine(x + bitWidth / 2, endY, x + bitWidth, endY);

            previousEndY = endY;
            x += bitWidth;
        }
    }

    private void drawManchesterDifferential(Graphics2D g2d, String chaine, int bitWidth, int y0V, int yNV, int yMinusNV) {
        g2d.setColor(SIGNAL_COLOR);
        g2d.setStroke(new BasicStroke(2.0f));
        int x = MARGIN;
        int currentY = yNV; // État initial conventionnel

        for (int i = 0; i < chaine.length(); i++) {
            char bit = chaine.charAt(i);
            
            if (bit == '1') {
                // 1 = Pas de transition au début
                g2d.drawLine(x, currentY, x + bitWidth / 2, currentY);
                int nextY = (currentY == yNV) ? yMinusNV : yNV;
                g2d.drawLine(x + bitWidth / 2, currentY, x + bitWidth / 2, nextY);
                g2d.drawLine(x + bitWidth / 2, nextY, x + bitWidth, nextY);
                currentY = nextY;
            } else {
                // 0 = Transition au début
                int nextY = (currentY == yNV) ? yMinusNV : yNV;
                g2d.drawLine(x, currentY, x, nextY);
                g2d.drawLine(x, nextY, x + bitWidth / 2, nextY);
                g2d.drawLine(x + bitWidth / 2, nextY, x + bitWidth / 2, currentY); // Retour état précédent (transition milieu)
                g2d.drawLine(x + bitWidth / 2, currentY, x + bitWidth, currentY);
            }
            x += bitWidth;
        }
    }

    private void drawMiller(Graphics2D g2d, String chaine, int bitWidth, int y0V, int yNV, int yMinusNV) {
        g2d.setColor(SIGNAL_COLOR);
        g2d.setStroke(new BasicStroke(2.0f));
        int x = MARGIN;
        int currentY = yNV;

        for (int i = 0; i < chaine.length(); i++) {
            char bit = chaine.charAt(i);

            if (bit == '1') {
                // Transition au milieu
                g2d.drawLine(x, currentY, x + bitWidth / 2, currentY);
                int nextY = (currentY == yNV) ? yMinusNV : yNV;
                g2d.drawLine(x + bitWidth / 2, currentY, x + bitWidth / 2, nextY);
                g2d.drawLine(x + bitWidth / 2, nextY, x + bitWidth, nextY);
                currentY = nextY;
            } else {
                // 0 : Transition au début SEULEMENT si le précédent était aussi un 0
                if (i > 0 && chaine.charAt(i - 1) == '0') {
                    int nextY = (currentY == yNV) ? yMinusNV : yNV;
                    g2d.drawLine(x, currentY, x, nextY);
                    currentY = nextY;
                }
                g2d.drawLine(x, currentY, x + bitWidth, currentY);
            }
            x += bitWidth;
        }
    }
}
