package vermilion;

import java.util.ArrayList;
import javax.swing.JOptionPane;
import java.io.File;

/**
 * Handles opening and closing the application and its main window. This is
 * also a hub containing static references to all system classes. The classes
 * made available are:
 *
 * Main.window, the JFrame which holds the program and provider interfaces.
 * Main.audio, the audio thread which calls on system providers for audio.
 * Main.document, the UsefulFile containing the composition definition.
 * Main.settings, the UsefulFile containing the user's preferences.
 * Main.plugins, the class which provides system and 3rd party runtime classes.
 *
 * The framework has the capacity to be reloaded at runtime. This allows the
 * user to change global preferences and apply them without restarting the
 * program. There are parts of the system dependent on preferences and parts
 * that are not, hence some parts of the system must be recycled and others
 * must not.
 *
 * The procedure for framework construction/recycling is:
 * 1) setupFramework creates main window and plugin manager.
 * 2) startFramework populates main window, starts audio, loads system plugins.
 * 3) stopFramework stops audio and unloads system plugins.
 * 4) closeFramework closes the main window and exits OR return to 2.
 */
public class Main
{
    /**
     * The name of the preferences file we load at startup. This is placed
     * in the user's data directory in a subfolder called 'vermilion'.
     */
    public static final String PREFS_FILENAME = "preferences.ufp";

    /**
     * A message we can display to the user when the main window opens.
     */
    public static final String WELCOME_MESSAGE = ""
        + "Welcome to Vermilion 0.04 Alpha\n"
        + "\n"
        + "This program is in very early development. Thanks for checking\n"
        + "it out. If you have an idea or spot a major bug, please post it on\n"
        + "the project forum at\n"
        + "\n"
        + "http://sourceforge.net/projects/buzz-like\n"
        + "\n"
        + "Please note that while Vermilion is in alpha, no guarantee is\n"
        + "made that you can load files made with previous versions.\n"
        ;

    /**
     * The main window of the application.
     */
    public static MainFrame window = null;

    /**
     * A double buffered audio thread.
     */
    public static AudioThread audio = null;
    
    /**
     * The user's currently open document.
     */
    public static UsefulFile document = null;

    /**
     * The program's preferences.
     */
    public static UsefulFile settings = null;

    /**
     * The plugin provider class.
     */
    public static SystemPluginManager plugins = null;

    /**
     * Entry point for the progam, begins initialisation. Command line arguments
     * are currently ignored. TODO: Accept filename and/or framework startup
     * plugin parameters.
     * @param args Command line arguments, which we currently ignore.
     */
    public static void main(String[] args)
    {
        setupFramework();
    }

    /**
     * Creates the public static systems that make up the application,
     * including the main window, audio output thread and user document.
     * Also creates support classes and a preferences 'document'.
     */
    private static void setupFramework()
    {
        // The very first thing we do is make the system preferences available.
        // See the UsefulFile class for more information on how this is done.
        // First, register basic object types for reading and writing prefs.
        UsefulFile.registerType(new UsefulFileObjectSimple(-1));
        UsefulFile.registerType(new UsefulFileObjectList());
        UsefulFile.registerType(new UsefulFileObjectIntMap());

        // Create a class representing the program preferences. This loads
        // the user's file or a default preferences file found in the program
        // directory. The user preferences file is only created once the user
        // changes a setting.
        setupPrefsFile();

        // Next we create the main window, attach a close handler and show it.
        // The window has no controls at this stage and is opened only so that
        // if an error dialog needs to be shown we don't have a focus problem.
        window = new MainFrame();
        window.setVisible(true);

        // Next, we create a provider of runtime-compiled classes. This is the
        // place where most system functions come from. At this stage all we
        // are doing is enumerating the plugin directory.
        plugins = new SystemPluginManager();

        // Now we can create a class representing the currently open document.
        // We require that system plugins be able to start with a completely
        // blank document, creating the structure and initialising themselves/it
        // with any required data.
        document = new UsefulFile(false);

        // Create the audio output thread. This simply creates the placeholder
        // so we may refer to it during startup for constants.
        audio = new AudioThread();

        // Does setup of window, setting up preferences tab controls. At this
        // point the window can be used to configure sound and select system
        // startup plugins.
        window.initialise();

        // Start the dynamic parts of the framework. This is where startup
        // plugins load and create their interfaces.
        startFramework();

        // Show a welcome message to the user if not disabled.
        UsefulFileObjectSimple popupPref = (UsefulFileObjectSimple) Main.settings.getValue("Startup Popup Setting");
        if (popupPref != null && popupPref.longValue == 1)
        {
            JOptionPane.showMessageDialog(Main.window, WELCOME_MESSAGE, "Welcome to Vermilion", JOptionPane.INFORMATION_MESSAGE);
        }

        // Load a demo song here if the user has not disabled the function.
        UsefulFileObjectSimple demoPref = (UsefulFileObjectSimple) Main.settings.getValue("Startup Sound Setting");
        if (demoPref != null && demoPref.longValue == 1)
        {
            load("songs/default.ufm");
            document.currentFilename = "";
        }
    }

    protected static void startFramework()
    {
        // Start the audio thread. TODO: Why does doing this last stop sound?
        if (audio != null) audio.start();

        // If we got a plugin provider, kick off the plugins.
        if (plugins != null)
        {
            // Call initialise on startup plugins, allowing them to add
            // a tab to the main window, connect to the audio thread etc.
            plugins.loadSystem();

            // Notify all the system plugins that a document has been loaded.
            plugins.documentLoaded();
        }
    }


    protected static void stopFramework()
    {
        // Close any open dialogs.
        if (window != null) window.closeAllDialogs();

        // Stop all system plugins.
        if (plugins != null) plugins.unloadSystem();

        // Stop the audio output thread.
        if (audio != null) audio.stop();
    }

    /**
     * Closes down the framework after checking with the user to save their
     * document. If the user does not cancel, stops all running threads and
     * releases all system resources.
     */
    public static void closeFramework()
    {
        if (window != null && window.checkSaving() == false) return;

        stopFramework();

        System.exit(0);
    }

    /**
     * Loads the program preferences file. The preferences are placed in the
     * user.home directory in a subdirectory called vermilion. If this doesn't
     * exist it is created. If the file does not exist, a default file will be
     * loaded and placed in the subdirectory.
     */
    public static void setupPrefsFile()
    {
        settings = new UsefulFile(true);

        try
        {
            String userDir = System.getProperty("user.home");
            String prefsDir = userDir + File.separator + "vermilion" + File.separator;

            try
            {
                File f = new File(prefsDir);
                if (!f.exists()) f.mkdir();
            }
            catch (Exception ex) {}

            String prefsFile = prefsDir + PREFS_FILENAME;

            // If the prefs file doesn't exist, open a file from our own
            // directory as a default, saving back to the user's dir.
            File g = new File(prefsFile);
            if (g.exists())
            {
                boolean success = settings.load(prefsFile);
                if (!success)
                {
                    settings = new UsefulFile(true);
                    settings.saveAs(prefsFile);
                }
            }
            else
            {
                 boolean success = settings.load("defaultprefs.ufp");
                if (!success)
                {
                    settings = new UsefulFile(true);
                }

                settings.saveAs(prefsFile);
            }
        }
        catch (Exception ex) { }
    }

    // TODO: Utilities class?
    public static String chooseName(String baseName, ArrayList<String> existingNames)
    {
        boolean nameChosen = false;
        boolean nameFound = false;

        int y = 1;
        String chosenName = baseName + String.valueOf(y);

        while (nameChosen == false)
        {
            nameFound = false;
            for (int x = 0; x < existingNames.size(); x++)
            {
                if (existingNames.get(x).equals(chosenName))
                {
                    nameFound = true;
                }
            }

            y++;

            if (nameFound == false)
            {
                nameChosen = true;
            }
            else
            {
                chosenName = baseName + String.valueOf(y);
            }
        }
        return chosenName;
    }

    // TODO: put save and new here as well, sort logic from MainFrame.
    /**
     * Loads a document. This includes notifying system components.
     */
    public static void load(String fileName)
    {
        if (fileName != null) Main.document.load(fileName);
        else Main.document = new UsefulFile(false);

        Main.plugins.documentLoaded();
        
        // Because we are writing to the document during docloaded, we
        // ignore these 'updates' as far as saving is concerned.
        Main.document.needsSaving = false;
    }
}

