import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;

public class JuliaFractal extends JPanel implements MouseListener {
    private static final int WIDTH = 800;
    private static final int HEIGHT = 800;
    private static final int MAX_ITER = 256;
    
    // Julia set constant
    private double cRe = -0.7;
    private double cIm = 0.27015;
    
    // Viewing window
    private double minRe = -1.5;
    private double maxRe = 1.5;
    private double minIm = -1.5;
    private double maxIm = 1.5;
    
    private BufferedImage img;
    
    public JuliaFractal() {
        setPreferredSize(new Dimension(WIDTH, HEIGHT));
        addMouseListener(this);
        img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        renderFractal();
    }
    
    private void renderFractal() {
        double reRange = maxRe - minRe;
        double imRange = maxIm - minIm;
        
        for (int y = 0; y < HEIGHT; y++) {
            for (int x = 0; x < WIDTH; x++) {
                double re = minRe + (x * reRange) / WIDTH;
                double im = minIm + (y * imRange) / HEIGHT;
                
                int iter = julia(re, im);
                int color = getColor(iter);
                img.setRGB(x, y, color);
            }
        }
        repaint();
    }
    
    private int julia(double zRe, double zIm) {
        int n = 0;
        while (n < MAX_ITER) {
            double zRe2 = zRe * zRe;
            double zIm2 = zIm * zIm;
            
            if (zRe2 + zIm2 > 4.0) break;
            
            double newRe = zRe2 - zIm2 + cRe;
            double newIm = 2 * zRe * zIm + cIm;
            
            zRe = newRe;
            zIm = newIm;
            n++;
        }
        return n;
    }
    
    private int getColor(int iter) {
        if (iter == MAX_ITER) return 0x000000;
        
        double t = (double) iter / MAX_ITER;
        int r = (int) (9 * (1 - t) * t * t * t * 255);
        int g = (int) (15 * (1 - t) * (1 - t) * t * t * 255);
        int b = (int) (8.5 * (1 - t) * (1 - t) * (1 - t) * t * 255);
        
        return (r << 16) | (g << 8) | b;
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, 0, 0, null);
    }
    
    @Override
    public void mouseClicked(MouseEvent e) {
        double reRange = maxRe - minRe;
        double imRange = maxIm - minIm;
        
        double clickRe = minRe + (e.getX() * reRange) / WIDTH;
        double clickIm = minIm + (e.getY() * imRange) / HEIGHT;
        
        double zoomFactor = 0.5;
        double newReRange = reRange * zoomFactor;
        double newImRange = imRange * zoomFactor;
        
        minRe = clickRe - newReRange / 2;
        maxRe = clickRe + newReRange / 2;
        minIm = clickIm - newImRange / 2;
        maxIm = clickIm + newImRange / 2;
        
        renderFractal();
    }
    
    @Override
    public void mousePressed(MouseEvent e) {}
    
    @Override
    public void mouseReleased(MouseEvent e) {}
    
    @Override
    public void mouseEntered(MouseEvent e) {}
    
    @Override
    public void mouseExited(MouseEvent e) {}
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Fraktal Julii - Kliknij aby powiększyć");
            JuliaFractal fractal = new JuliaFractal();
            frame.add(fractal);
            frame.pack();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}