package me.teridax.jcash;

import me.teridax.jcash.gui.IconProvider;
import me.teridax.jcash.gui.Loader;
import me.teridax.jcash.gui.MainFrame;
import me.teridax.jcash.gui.Utils;
import me.teridax.jcash.lang.Locales;

import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.util.Objects;
import java.util.logging.Level;

import static me.teridax.jcash.Logging.LOGGER;
import static me.teridax.jcash.Logging.initializeSystemLogger;

public final class Main {

    /**
     * Main instance of this program. Contains the primary window.
     */
    private static Main instance;

    /**
     * Primary class for controlling GUI of this application
     */
    private final MainFrame window;

    private Main() {
        this.window = new MainFrame();
    }

    /**
     * Prompts the user a dialog to select a file to load the database from.
     * If a valid database has been read a login screen will be shown.
     * If no file was selected or the database was invalid the application will close.
     */
    public void loadDatabase() {
        try {
            var bms = Loader.load();
            this.window.setBms(bms);

        } catch (Exception e) {
            LOGGER.severe("Failed to load database: " + e.getMessage());
            Utils.error("Failed to load database");
            System.exit(1);
        }
    }

    public static void main(String[] args) {
        initializeSystemLogger(Level.FINE);

        setPlatformDependingTheme();

        loadExtraFont();

        Locales.autodetectDefaultLocale();

        // create main instance and show the login screen
        instance();
        getInstance().loadDatabase();
    }

    /**
     * Loads the extra font file used on the login button
     */
    private static void loadExtraFont() {
        try {
            var font = Font.createFont(Font.TRUETYPE_FONT, Objects.requireNonNull(IconProvider.class.getResourceAsStream("res/Circus.ttf")));
            GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            ge.registerFont(font);
        } catch (IOException | FontFormatException | NullPointerException e) {
            LOGGER.warning("Could not load font file: " + e.getMessage());
        }
    }

    /**
     * Set the look and feel via the ui manager.
     * This function will select a look and feel so that the application will
     * look most like a native application.
     * It will select the systems look and feel for Windows and MacOS
     * and GTK for unix based systems.
     */
    private static void setPlatformDependingTheme() {
        // default look and feel
        var laf = UIManager.getCrossPlatformLookAndFeelClassName();

        // runtime os
        String os = System.getProperty("os.name").toLowerCase();
        LOGGER.fine("Detected operating system: " + os);

        // set look and feel class name depending on the platform we run on
        if (os.contains("win") || os.contains("mac")) {
            LOGGER.info("Detected Windows or MacOS based operating system, using system look and feel");
            laf = UIManager.getSystemLookAndFeelClassName();
        } else if (os.contains("nix") || os.contains("nux") || os.contains("aix") || os.contains("sunos")) {
            LOGGER.info("Detected Unix/Linux based operating system, using GTK look and feel");
            laf = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
        } else {
            LOGGER.warning("Unable to detect operating system: " + os);
        }

        try {
            LOGGER.info("Setting look and feel to class: " + laf);
            UIManager.setLookAndFeel(laf);
        } catch (ClassNotFoundException | UnsupportedLookAndFeelException e) {
            LOGGER.severe("Look and feel class not found: " + e.getMessage());
        } catch (InstantiationException | IllegalAccessException e) {
            LOGGER.warning("Could not set look and feel: " + e.getMessage());
        }
    }

    /**
     * Get the main singleton instance of this program
     *
     * @return the singleton instance of this class
     */
    public static Main getInstance() {
        return instance;
    }

    /**
     * Attempts to create a new instance of the singleton class Main.
     * This method throws an exception if the class is already instantiated.
     *
     * @throws IllegalStateException if the class is already instantiated
     */
    public static void instance() {
        if (null != instance)
            throw new IllegalStateException(Main.class.getName() + " is already initialized");

        LOGGER.fine("Creating singleton instance of class " + Main.class.getName());

        Main.instance = new Main();
    }

    public JFrame getWindow() {
        return this.window.getWindow();
    }

    /**
     * Logs the user out of the currently open account.
     * This will show the login mask and clear the password field or the previous
     * login attempt.
     */
    public void logout() {
        this.window.logout();
    }
}