package com.axis.innovators.box;

import com.axis.innovators.box.decompilation.gui.ModernJarViewer;
import com.axis.innovators.box.events.GlobalEventBus;
import com.axis.innovators.box.events.OpenFileEvents;
import com.axis.innovators.box.events.StartupEvent;
import com.axis.innovators.box.gui.*;
import com.axis.innovators.box.plugins.PluginDescriptor;
import com.axis.innovators.box.plugins.PluginLoader;
import com.axis.innovators.box.plugins.PluginPyLoader;
import com.axis.innovators.box.register.RegistrationSettingsItem;
import com.axis.innovators.box.register.RegistrationTool;
import com.axis.innovators.box.register.RegistrationTopic;
import com.axis.innovators.box.register.LanguageManager;
import com.axis.innovators.box.tools.ArgsParser;
import com.axis.innovators.box.tools.LibraryLoad;
import com.axis.innovators.box.tools.StateManager;
import com.axis.innovators.box.tools.SystemInfoUtil;
import com.axis.innovators.box.util.PythonResult;
import com.axis.innovators.box.util.UserLocalInformation;
import com.axis.innovators.box.verification.UserTags;
import com.formdev.flatlaf.FlatLightLaf;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.tzd.lm.LM;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 主类
 * @author tzdwindows 7
 */
public class AxisInnovatorsBox {
    private static final Logger logger = LogManager.getLogger(AxisInnovatorsBox.class);
    private static final String VERSIONS = "0.0.2";
    private static final String[] AUTHOR = new String[]{
            "tzdwindows 7"
    };

    /** 我是总任务数 **/
    public int totalTasks = 1;
    /** 我是当前任务数 **/
    private int completedTasks = 0;
    public ProgressBarManager progressBarManager = new ProgressBarManager(
            LanguageManager.getLoadedLanguages().getText("progressBarManager.title"),
            totalTasks);
    private static AxisInnovatorsBox main;
    private MainWindow ex;
    private Thread thread;
    private final String[] args;
    private boolean isWindow = false;
    private RegistrationTool registrationTool = new RegistrationTool(this);
    private final RegistrationTopic registrationTopic = new RegistrationTopic(this);
    private final List<WindowsJDialog> windowsJDialogList = new ArrayList<>();
    private final StateManager stateManager = new StateManager();
    private UserTags userTags;

    public AxisInnovatorsBox(String[] args) {
        this.args = args;
    }


    static {
        try {
            LibraryLoad.loadLibrary("FridaNative");
            LibraryLoad.loadLibrary("ThrowSafely");
        } catch (Exception e) {
            logger.error("Failed to load the 'FridaNative' library", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取主类
     * @return 主类
     */
    public static AxisInnovatorsBox getMain() {
        return main;
    }

    /**
     * 获取主窗口
     * @return 主窗口
     */
    public MainWindow getMainWindow() {
        return ex;
    }

    /**
     * 退出程序
     */
    public void quit() {
        logger.info("Application is shutting down...");
        thread.interrupt();
        System.exit(0);
    }

    /**
     * 组织崩溃报告
     */
    public void organizingCrashReports(Exception e) {
        String systemOut = Log4j2OutputStream.systemOutContent.toString();
        String systemErr = Log4j2OutputStream.systemErrContent.toString();

        StringWriter sw = new StringWriter();
        e.printStackTrace(new PrintWriter(sw));
        String stackTrace = sw.toString();

        String report = "========== 系统标准输出 ==========\n" + systemOut +
                "\n========== 系统错误输出 ==========\n" + systemErr +
                "\n========== 异常堆栈跟踪 ==========\n" + stackTrace;

        SwingUtilities.invokeLater(() -> createAndShowGUI(report));
    }

    private void createAndShowGUI(String reportContent) {
        JDialog dialog = new JDialog((Frame) null,
                LanguageManager.getLoadedLanguages().getText("andShow.title"), true);
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        dialog.setLayout(new BorderLayout(10, 10));

        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ignored) {}

        JPanel topPanel = new JPanel(new BorderLayout(10, 10));
        Icon errorIcon = UIManager.getIcon("OptionPane.errorIcon");
        JLabel iconLabel = new JLabel(errorIcon);

        JLabel titleLabel = new JLabel(LanguageManager.getLoadedLanguages().getText("andShow.title.2"));

        JLabel feedbackLabel = new JLabel(LanguageManager.getLoadedLanguages().getText("andShow.title.3"));
        feedbackLabel.setHorizontalAlignment(SwingConstants.CENTER);

        JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10));
        titlePanel.add(iconLabel);
        titlePanel.add(titleLabel);

        topPanel.add(titlePanel, BorderLayout.NORTH);
        topPanel.add(feedbackLabel, BorderLayout.CENTER);
        topPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

        JTextArea contentArea = new JTextArea(reportContent);
        contentArea.setEditable(false);
        contentArea.setFont(new Font("Monospaced", Font.PLAIN, 12));

        StringBuilder pluginInfo = new StringBuilder();
        pluginInfo.append(
                LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title"))
                .append("\n");

        List<PluginDescriptor> corePluginList = new ArrayList<>();
        for (PluginDescriptor plugin : PluginLoader.getLoadedPlugins()) {
            pluginInfo.append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title.1")).append(plugin.getName()).append("\n");
            pluginInfo.append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title.2")).append(plugin.getDescription()).append("\n");
            pluginInfo.append(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.title.3")).append(plugin.getSupportedVersions()).append("\n\n");
            if (plugin.getTransformers() != null) {
                corePluginList.add(plugin);
            }
        }

        pluginInfo.append("=== 核心插件 ===\n");
        for (PluginDescriptor corePlugin : corePluginList) {
            pluginInfo.append("核心插件主类: ").append(corePlugin.getName()).append("\n");
            pluginInfo.append("核心转换器位置: ").append(corePlugin.getTransformers()).append("\n");
        }

        contentArea.append("\n\n=== 插件信息 ===\n");
        contentArea.append(pluginInfo.toString());

        JScrollPane scrollPane = new JScrollPane(contentArea);
        scrollPane.setBorder(BorderFactory.createTitledBorder("错误详细信息"));

        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10));
        JButton exportButton = new JButton(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.exportButton"));
        JButton closeButton = new JButton(LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.closeButton"));

        exportButton.setForeground(Color.BLACK);
        closeButton.setForeground(Color.BLACK);

        exportButton.setBackground(new Color(0, 120, 215));
        exportButton.setFocusPainted(false);
        closeButton.setBackground(new Color(79, 79, 79));
        closeButton.setFocusPainted(false);

        exportButton.addActionListener((ActionEvent e) -> {
            JFileChooser fileChooser = new JFileChooser();
            fileChooser.setDialogTitle("保存崩溃报告");
            fileChooser.setSelectedFile(new File("crash_report.txt"));

            int userSelection = fileChooser.showSaveDialog(dialog);
            if (userSelection == JFileChooser.APPROVE_OPTION) {
                File fileToSave = fileChooser.getSelectedFile();
                try (FileWriter writer = new FileWriter(fileToSave)) {
                    writer.write(reportContent + "\n" + pluginInfo);
                    JOptionPane.showMessageDialog(dialog,
                            LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer")
                                    + "\n" + fileToSave.getAbsolutePath(),
                            LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer.title"),
                            JOptionPane.INFORMATION_MESSAGE);
                } catch (IOException ex) {
                    JOptionPane.showMessageDialog(dialog,
                            LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.writer.error")
                                    + "\n" + ex.getMessage(),
                            LanguageManager.getLoadedLanguages().getText("andShow.pluginInfo.error.writer.title"),
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        });

        closeButton.addActionListener(e -> quit());

        buttonPanel.add(exportButton);
        buttonPanel.add(closeButton);

        dialog.add(topPanel, BorderLayout.NORTH);
        dialog.add(scrollPane, BorderLayout.CENTER);
        dialog.add(buttonPanel, BorderLayout.SOUTH);

        dialog.setSize(800, 600);
        dialog.setLocationRelativeTo(null);
        dialog.setVisible(true);
    }

    /**
     * 初始化Log4j2
     */
    public void initLog4j2() {

        logger.info("Application Version: {}", VERSIONS);
        logger.info("Authors: {}", String.join(", ", AUTHOR));

        logger.info("Operating System: {}", SystemInfoUtil.getOSName());
        logger.info("OS Architecture: {}", SystemInfoUtil.getOSArch());
        logger.info("CPU: {}", SystemInfoUtil.getCPUInfo());
        logger.info("GPU: {}", SystemInfoUtil.getGPUInfo());
        logger.info("Java Version: {}", System.getProperty("java.version"));
        logger.info("Java Vendor: {}", System.getProperty("java.vendor"));
        logger.info("Java Home: {}", System.getProperty("java.home"));
        logger.info("Java Class Path: {}", System.getProperty("java.class.path"));
        logger.info("ClassLoader.getSystemClassLoader(): {}", ClassLoader.getSystemClassLoader());

        Log4j2OutputStream.redirectSystemStreams();
    }

    /**
     * 设置主题
     */
    private void setTopic() {
        try {
            main.registrationTopic.addTopic(new com.formdev.flatlaf.FlatDarculaLaf(),
                    "Darcula主题",
                    "Darcula主题",
                    LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
                    "system:darcula_theme");

            main.registrationTopic.addTopic(UIManager.getSystemLookAndFeelClassName(),
                    LanguageManager.getLoadedLanguages().getText("default_theme.system.topicName"),
                    LanguageManager.getLoadedLanguages().getText("default_theme.default.tip"),
                    LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
                    "system:default_theme");
            main.registrationTopic.setLoading("system:default_theme");

            main.registrationTopic.addTopic("javax.swing.plaf.metal.MetalLookAndFeel",
                    LanguageManager.getLoadedLanguages().getText("metal_theme.system.topicName"),
                    LanguageManager.getLoadedLanguages().getText("metal_theme.default.tip"),
                    LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
                    "system:metal_theme");

            main.registrationTopic.addTopic("com.sun.java.swing.plaf.motif.MotifLookAndFeel",
                    LanguageManager.getLoadedLanguages().getText("motif_theme.system.topicName"),
                    LanguageManager.getLoadedLanguages().getText("motif_theme.default.tip"),
                    LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
                    "system:motif_theme");

            main.registrationTopic.addTopic(new FlatLightLaf(),
                    LanguageManager.getLoadedLanguages().getText("flatLightLaf_theme.system.topicName"),
                    LanguageManager.getLoadedLanguages().getText("flatLightLaf_theme.default.tip"),
                    LoadIcon.loadIcon(MainWindow.class, "logo.png", 64),
                    "system:flatLightLaf_theme");

            UIManager.setLookAndFeel(new com.formdev.flatlaf.FlatDarculaLaf());
        } catch (Exception e) {
            logger.warn("Failed to load the system facade class", e);
        }
    }

    /**
     * 弹出窗口
     * @param windowsJDialog 窗口
     */
    public void popupWindow(WindowsJDialog windowsJDialog) {
        windowsJDialogList.add(windowsJDialog);
        windowsJDialog.setVisible(true);
    }

    /**
     * 判断窗口是否启动
     * @param windowsJDialog 窗口
     */
    public boolean isWindowStartup(WindowsJDialog windowsJDialog) {
        return windowsJDialogList.contains(windowsJDialog);
    }

    /**
     * 清除窗口
     * @param windowsJDialog 窗口
     */
    public void clearWindow(WindowsJDialog windowsJDialog){
        windowsJDialogList.remove(windowsJDialog);
        windowsJDialog.revalidate();
        windowsJDialog.repaint();
    }

    /**
     * 重新加载窗口
     */
    public void reloadAllWindow() {
        isWindow = false;
        ex.getContentPane().removeAll();
        ex.revalidate();
        ex.repaint();
        registrationTool = new RegistrationTool(this);
        for (WindowsJDialog windowsJDialog : windowsJDialogList) {
            windowsJDialog.getContentPane().removeAll();
            windowsJDialog.initUI();
            windowsJDialog.revalidate();
            windowsJDialog.repaint();
        }
        ex.initUI();
        ex.revalidate();
        RegistrationSettingsItem.overloading();
        isWindow = true;
    }

    public static void run(String[] args) {
        main = new AxisInnovatorsBox(args);
        try {
            main.initLog4j2();
            main.setTopic();

            List<Map<String, String>> validFiles = ArgsParser.parseArgs(args);
            for (Map<String, String> fileInfo : validFiles) {
                String extension = fileInfo.get("extension");
                String path = fileInfo.get("path");
                OpenFileEvents openFileEvents = new OpenFileEvents(path, extension);
                GlobalEventBus.EVENT_BUS.post(openFileEvents);
                if (!openFileEvents.isContinue()) {
                    return;
                }
            }

            main.thread = new Thread(() -> {
                try {
                    UserLocalInformation userLocalInformation = new UserLocalInformation(main);
                    main.userTags = userLocalInformation.getUserTags();
                    if (main.userTags == null) {
                        // 登录窗口
                        main.userTags = LoginWindow.createAndShow();
                        userLocalInformation.setUserTags(main.userTags);
                    }

                    // 主任务1：加载插件
                    logger.info("Loaded plugins Started");
                    main.progressBarManager.updateMainProgress(++main.completedTasks);
                    PluginLoader.loadPlugins();
                    PluginPyLoader.loadAllPlugins();
                    logger.info("Loaded plugins End");

                    main.progressBarManager.close();

                    SwingUtilities.invokeLater(() -> {
                        try {
                            main.ex = new MainWindow();
                            GlobalEventBus.EVENT_BUS.post(new StartupEvent(main));
                            PluginPyLoader.getLoadedPlugins().forEach((pluginId, pyPluginDescriptor) -> {
                                if (pyPluginDescriptor.getLastResult().getInterpreter() != null){
                                    PythonResult result = pyPluginDescriptor.getLastResult();
                                    try {
                                        result.callFunction("onStartup");
                                    } catch (Exception e) {
                                        logger.error("Failed to call onStartup function", e);
                                    }
                                }
                            });
                            main.runWindow();
                        } catch (Exception e) {
                            logger.error("There was a problem starting the main thread", e);
                            main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                            main.organizingCrashReports(e);
                            throw new RuntimeException(e);
                        }
                    });
                } catch (Exception e) {
                    logger.error("Failed to load plugins", e);
                    main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    main.organizingCrashReports(e);
                    throw new RuntimeException(e);
                }
            });
            main.thread.setName("Main Thread");
            main.thread.start();
        } catch (Exception e) {
            logger.error("In unexpected errors", e);
            main.ex.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            main.organizingCrashReports(e);
            throw new RuntimeException(e);
        }
    }

    public void runWindow() {
        progressBarManager.updateMainProgress(++completedTasks);

        if (!registrationTool.getToolCategories().isEmpty()) {
            for (int i = 0; i < registrationTool.getToolCategories()
                    .size(); i++) {
                ex.addToolCategory(registrationTool.getToolCategories()
                        .get(i));
                progressBarManager.updateSubProgress(
                        "add tools", i,
                        registrationTool.getToolCategories().size());
            }
        }

        ex.initUI();
        isWindow = true;
        ex.setVisible(true);
    }

    /**
     * 获取注册工具
     * @return 注册工具
     */
    public RegistrationTool getRegistrationTool() {
        return registrationTool;
    }

    /**
     * 获取命令行参数
     * @return 命令行参数
     */
    public String[] getArgs() {
        return args;
    }

    public boolean isWindow() {
        return isWindow;
    }

    public static String getVersion() {
        return VERSIONS;
    }

    public RegistrationTopic getRegistrationTopic() {
        return registrationTopic;
    }

    public static String[] getAuthor() {
        return AUTHOR;
    }

    /**
     * 获取用户标签
     * @return 用户标签
     */
    public UserTags getUserTags() {
        return userTags;
    }

    /**
     * 获取状态管理器
     * @return 状态管理器
     */
    public StateManager getStateManager() {
        return stateManager;
    }
}