package me.teridax.jcash.gui;

import me.teridax.jcash.banking.management.BankingManagementSystem;
import me.teridax.jcash.gui.account.AccountController;
import me.teridax.jcash.gui.login.LoginController;
import me.teridax.jcash.lang.Locales;

import javax.swing.*;
import java.awt.*;
import java.util.Objects;

import static me.teridax.jcash.Logging.LOGGER;
import static me.teridax.jcash.lang.Translator.translate;

public class MainFrame {

    /**
     * Constant used to identify the login screen on the cardlayout
     */
    private static final String LOGIN_SCREEN_STRING_IDENT = "LoginScreen";
    /**
     * Constant used to identify the profile screen on the cardlayout
     */
    private static final String PROFILE_SCREEN_STRING_IDENT = "ProfileScreen";
    /**
     * Version of this application
     */
    private static final String VERSION = "v2.1.0";

    /**
     * Primary window of this program
     */
    private final JFrame window;
    /**
     * Primary layout of this application
     */
    private final CardLayout layout;

    /**
     * Database containing every bank, account and owner available
     */
    private BankingManagementSystem bms;

    private LoginController loginMask;
    private AccountController accountController;

    public MainFrame() {
        // create main window and set defaults
        this.window = new JFrame();
        this.window.setTitle(translate("Cashmachine") + getInfoString());
        this.window.setLocationByPlatform(true);
        this.window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.window.setIconImage(IconProvider.getWindowIcon());

        this.layout = new CardLayout();
        this.window.getContentPane().setLayout(this.layout);

        initialize();
    }


    /**
     * Creates and returns a general information string about this application
     * @return the locale and the current version as string
     */
    private String getInfoString() {
        return " locale: [" + Locales.getDefaultLocale().toString() + "] " + VERSION;
    }

    /**
     * Initializes the GUI components of login screen and profile view
     */
    private void initialize() {
        // create the login mask

        this.loginMask = new LoginController();

        // when we have logged in set the account viewer as window content
        this.loginMask.addAccountSelectionListener(account -> {
            LOGGER.finer("account selected: " + Objects.toString(account, "null"));
            accountController.setProfile(account, bms);
            layout.show(window.getContentPane(), PROFILE_SCREEN_STRING_IDENT);
        });

        this.window.getContentPane().add(loginMask.getView(), LOGIN_SCREEN_STRING_IDENT);

        // create the account viewer

        this.accountController = new AccountController();
        this.window.getContentPane().add(accountController.getView(), PROFILE_SCREEN_STRING_IDENT);
    }

    /**
     * Sets the BMS of this application to use for the GUI.
     * This method will show the login screen to the user
     * @param bms the BMS to use for the GUI
     */
    public void setBms(BankingManagementSystem bms) {
        this.bms = bms;
        this.loginMask.setBankingManagementSystem(bms);
        this.showLoginScreen();
        this.window.pack();
        this.window.setResizable(false);
        this.window.setLocationRelativeTo(null);
        this.window.setVisible(true);
    }

    /**
     * Shows the open dialog for selecting a database file. After selection, it then proceeds to prompt login.
     * Afterward the selected account can be managed.
     * This method is non-blocking and all work described is performed asynchronously on the AWT Event dispatcher.
     */
    private void showLoginScreen() {
        this.layout.show(this.window.getContentPane(), LOGIN_SCREEN_STRING_IDENT);
    }

    /**
     * Logs the user out of the database, hiding the main window.
     */
    public void logout() {
        this.loginMask.logout();
        this.layout.show(this.window.getContentPane(), LOGIN_SCREEN_STRING_IDENT);
    }

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