SwingUI.java

package com.example.renderer.ui;

/**
 * SwingUI是图形渲染系统的主界面,使用Swing实现。
 * 
 * <p>主要功能组件:
 * <ul>
 *   <li>绘图面板(DrawingPanel) - 显示图形</li>
 *   <li>控制按钮 - 添加圆形、矩形、椭圆和三角形</li>
 *   <li>撤销/重做功能 - 通过UndoManager实现命令历史管理</li>
 *   <li>文件操作 - 通过PersistenceManager实现图形的保存和加载</li>
 * </ul>
 * 
 * <p>设计模式应用:
 * <ul>
 *   <li>命令模式 - 通过Command接口实现操作封装</li>
 *   <li>桥接模式 - 通过Renderer接口分离渲染逻辑</li>
 *   <li>单例模式 - 使用PersistenceManager管理持久化</li>
 *   <li>观察者模式 - 图形变化时自动重绘</li>
 * </ul>
 *
 * <p>典型使用场景:
 * <pre>{@code
 * SwingUI ui = new SwingUI(); // 创建并显示界面
 * }</pre>
 * 
 * @see DrawingPanel 绘图面板实现
 * @see UndoManager 撤销/重做管理
 * @see PersistenceManager 持久化管理器
 * @author liying
 * @since 1.0
 * @version 1.0.0
 */

import com.example.renderer.bridge.Renderer;
import com.example.renderer.bridge.RendererFactory;
import com.example.renderer.config.GlobalConfig;
import com.example.renderer.exception.RendererCreationException;
import com.example.renderer.factory.*;
import com.example.renderer.command.*;
import com.example.renderer.singleton.PersistenceManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import com.example.renderer.factory.Circle;
import com.example.renderer.factory.Rectangle;
import com.example.renderer.factory.Ellipse;
import com.example.renderer.factory.Triangle;
import com.example.renderer.bridge.Renderer;
import com.example.renderer.bridge.SwingRenderer;
import com.example.renderer.command.AddShapeCommand;
import com.example.renderer.command.UndoManager;


//import com.example.renderer.persistence.PersistenceManager;
import com.example.renderer.singleton.PersistenceManager;



/**
 * SwingUI是图形渲染系统的主界面,使用Swing实现。
 * 
 * <p>主要功能组件:
 * <ul>
 *   <li>绘图面板(DrawingPanel) - 显示图形</li>
 *   <li>控制按钮 - 添加圆形、矩形、椭圆和三角形</li>
 *   <li>撤销/重做功能 - 通过UndoManager实现命令历史管理</li>
 *   <li>文件操作 - 通过PersistenceManager实现图形的保存和加载</li>
 * </ul>
 * 
 * @see DrawingPanel 绘图面板实现
 * @see UndoManager 撤销/重做管理
 * @see PersistenceManager 持久化管理器
 * @author liying
 * @since 1.0
 */
public class SwingUI extends JFrame {
    private static final Logger LOGGER = LoggerFactory.getLogger(SwingUI.class);
    /** 图形列表 */
    private final List<com.example.renderer.factory.Shape> shapes = new ArrayList<>();
    
    /** 渲染器实现 */
    private Renderer renderer;
    
    /** 撤销管理器 */
    private UndoManager undoManager = new UndoManager();
    
    /** 绘图面板组件 */
    private DrawingPanel drawingPanel;


    /**
     * 创建并初始化Swing图形用户界面。
     * 
     * <p>初始化内容包括:
     * <ul>
     *   <li>设置窗口标题和大小</li>
     *   <li>创建绘图面板</li>
     *   <li>添加各种图形按钮</li>
     *   <li>设置按钮事件监听器</li>
     *   <li>添加撤销/重做功能</li>
     *   <li>添加文件保存/加载功能</li>
     * </ul>
     */
    public SwingUI() {
        setTitle("图形渲染系统 - Swing 可视化");
        setSize(800, 600);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);


        // 通过配置获取渲染器
        String mode = GlobalConfig.getInstance().getRenderMode();
        try {
            renderer = RendererFactory.create(mode);
        } catch (RendererCreationException e) {
            LOGGER.error("Renderer initialization failed", e);
            JOptionPane.showMessageDialog(this,
                String.format("无法初始化渲染器 %s: %s\n将使用默认Swing渲染器",
                    mode, e.getMessage()),
                "渲染器错误", JOptionPane.ERROR_MESSAGE);
            renderer = createFallbackRenderer();
        }

        drawingPanel = new DrawingPanel(shapes, renderer);
        add(drawingPanel, BorderLayout.CENTER);  // 添加绘图面板到中心区域

        JButton btnCircle = new JButton("添加圆形");
        JButton btnRect = new JButton("添加矩形");
        JButton btnEllipse = new JButton("添加椭圆");
        JButton btnTriangle = new JButton("添加三角形");
        JButton btnUndo = new JButton("撤销");
        JButton btnRedo = new JButton("重做");

        JPanel panel = new JPanel();
        panel.add(btnCircle);
        panel.add(btnRect);
        panel.add(btnEllipse);
        panel.add(btnTriangle);
        panel.add(btnUndo);
        panel.add(btnRedo);

        // 添加圆形按钮事件处理
        btnCircle.addActionListener(e -> {
            // 创建圆形实例,位置和半径根据已有图形数量动态调整
            Circle c = new Circle(100 + shapes.size() * 10, 100, 30 + shapes.size() * 5);
            AddShapeCommand cmd = new AddShapeCommand(shapes, c);
            undoManager.executeCommand(cmd);
            repaint();
        });

        btnRect.addActionListener(e -> {
            Rectangle r = new Rectangle(200, 150 + shapes.size() * 10, 60, 40);
            AddShapeCommand cmd = new AddShapeCommand(shapes, r);
            undoManager.executeCommand(cmd);
            repaint();
        });

        btnEllipse.addActionListener(e -> {
            Ellipse e1 = new Ellipse(300, 200, 50, 30);
            AddShapeCommand cmd = new AddShapeCommand(shapes, e1);
            undoManager.executeCommand(cmd);
            repaint();
        });

        btnTriangle.addActionListener(e -> {
            Triangle t = new Triangle(400, 300, 450, 350, 420, 390);
            AddShapeCommand cmd = new AddShapeCommand(shapes, t);
            undoManager.executeCommand(cmd);
            repaint();
        });

        btnUndo.addActionListener(e -> {
            undoManager.undo();
            repaint();
        });

        btnRedo.addActionListener(e -> {
            undoManager.redo();
            repaint();
        });

        JButton btnSave = new JButton("保存图形");
        btnSave.addActionListener(e -> saveShapes());

        JButton btnLoad = new JButton("载入图形");
        btnLoad.addActionListener(e -> loadShapes());

        panel.add(btnSave);
        panel.add(btnLoad);

        add(panel, BorderLayout.SOUTH);

        pack();  // 让窗口根据内容尺寸调整
        setLocationRelativeTo(null);  // 窗口居中
        setVisible(true);


    }

    // 移除paint()方法重写,完全使用DrawingPanel进行绘制


    /**
     * 保存当前图形列表到文件。
     * 使用JFileChooser让用户选择保存位置,然后通过PersistenceManager
     * 将图形列表序列化为JSON格式保存。
     * 
     * <p>处理流程:
     * 1. 显示文件选择对话框
     * 2. 用户选择保存位置
     * 3. 调用PersistenceManager保存数据
     * 4. 显示操作结果提示
     * 
     * @see PersistenceManager#saveShapesToFile(List, String)
     */
    private void saveShapes() {
        JFileChooser chooser = new JFileChooser();
        int ret = chooser.showSaveDialog(this);
        if (ret == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile();
            try {
                PersistenceManager.getInstance().saveShapesToFile(shapes, file.getAbsolutePath());
                JOptionPane.showMessageDialog(this, "保存成功");
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(this, "保存失败: " + ex.getMessage());
            }
        }
    }


    /**
     * 从文件加载图形列表。
     * 使用JFileChooser让用户选择要加载的文件,然后通过PersistenceManager
     * 从JSON格式反序列化为图形对象列表。
     * 
     * <p>处理流程:
     * 1. 显示文件选择对话框
     * 2. 用户选择要加载的文件
     * 3. 调用PersistenceManager加载数据
     * 4. 清空当前图形列表并添加加载的图形
     * 5. 刷新绘图面板显示新图形
     * 6. 显示操作结果提示
     * 
     * @see PersistenceManager#loadShapesFromFile(String) 
     */
    private Renderer createFallbackRenderer() {
        try {
            return new SwingRenderer();
        } catch (Exception e) {
            LOGGER.error("Fallback renderer creation failed", e);
            throw new IllegalStateException("无法创建回退渲染器", e);
        }
    }

    private void loadShapes() {
        JFileChooser chooser = new JFileChooser();
        int ret = chooser.showOpenDialog(this);
        if (ret == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile();
            try {
                List<com.example.renderer.factory.Shape> loadedShapes = PersistenceManager.getInstance().loadShapesFromFile(file.getAbsolutePath());
                shapes.clear();
                shapes.addAll(loadedShapes);
                drawingPanel.repaint();  // 这里刷新绘图面板

                JOptionPane.showMessageDialog(this, "载入成功");
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(this, "载入失败: " + ex.getMessage());
            }
        }
    }

}