/*
 * interface.c
 * Copyright 2010-2014 John Lindgren
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions, and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions, and the following disclaimer in the documentation
 *    provided with the distribution.
 *
 * This software is provided "as is" and without any warranty, express or
 * implied. In no event shall the authors be liable for any damages arising from
 * the use of this software.
 */

#include "interface.h"
#include "internal.h"

#include <assert.h>

#include "drct.h"
#include "hook.h"
#include "mainloop.h"
#include "plugin.h"
#include "plugins.h"
#include "runtime.h"

struct MenuItem {
    const char * name;
    const char * icon;
    void (* func) ();
};

static PluginHandle * current_plugin;
static PluginHandle * next_plugin;

static IfacePlugin * current_interface;

static aud::array<AudMenuID, Index<MenuItem>> menu_items;

static void add_menu_items ()
{
    for (AudMenuID id : aud::range<AudMenuID> ())
    {
        for (MenuItem & item : menu_items[id])
            current_interface->plugin_menu_add (id, item.func, item.name, item.icon);
    }
}

static void remove_menu_items ()
{
    for (AudMenuID id : aud::range<AudMenuID> ())
    {
        for (MenuItem & item : menu_items[id])
            current_interface->plugin_menu_remove (id, item.func);
    }
}

static bool interface_load (PluginHandle * plugin)
{
    auto i = (IfacePlugin *) aud_plugin_get_header (plugin);
    if (! i)
        return false;

    AUDINFO ("Loading %s.\n", aud_plugin_get_name (plugin));

    if (! i->init ())
        return false;

    current_interface = i;

    add_menu_items ();

    if (aud_get_bool (0, "show_interface"))
        current_interface->show (true);

    return true;
}

static void interface_unload ()
{
    AUDINFO ("Unloading %s.\n", aud_plugin_get_name (current_plugin));

    if (aud_get_bool (0, "show_interface"))
        current_interface->show (false);

    remove_menu_items ();

    current_interface->cleanup ();
    current_interface = nullptr;
}

EXPORT void aud_ui_show (bool show)
{
    if (! current_interface)
        return;

    aud_set_bool (0, "show_interface", show);

    current_interface->show (show);

    vis_activate (show);
}

EXPORT bool aud_ui_is_shown ()
{
    if (! current_interface)
        return false;

    return aud_get_bool (0, "show_interface");
}

EXPORT void aud_ui_show_error (const char * message)
{
    if (aud_get_headless_mode ())
        AUDERR ("%s\n", message);
    else
        event_queue ("ui show error", String::raw_get (message),
         (EventDestroyFunc) String::raw_unref);
}

PluginHandle * iface_plugin_get_current ()
{
    return current_plugin;
}

bool iface_plugin_set_current (PluginHandle * plugin)
{
    if (current_interface)
    {
        // queue up interface switch
        next_plugin = plugin;

        // restart main loop
        aud_quit ();

        return true;
    }

    if (! interface_load (plugin))
        return false;

    current_plugin = plugin;
    return true;
}

void interface_run ()
{
    if (aud_get_headless_mode ())
    {
        mainloop_run ();

        // call before shutting down
        hook_call ("config save", nullptr);
    }
    else
    {
        vis_activate (aud_get_bool (0, "show_interface"));

        while (current_interface)
        {
            current_interface->run ();

            // call before unloading interface
            hook_call ("config save", nullptr);

            interface_unload ();

            if (next_plugin)
            {
                // handle queued interface switch
                aud_plugin_enable (next_plugin, true);
                next_plugin = nullptr;
            }
        }
    }
}

EXPORT void aud_quit ()
{
    if (current_interface)
        current_interface->quit ();
    else
        mainloop_quit ();
}

EXPORT void aud_plugin_menu_add (AudMenuID id, void (* func) (), const char * name, const char * icon)
{
    menu_items[id].append (name, icon, func);

    if (current_interface)
        current_interface->plugin_menu_add (id, func, name, icon);
}

EXPORT void aud_plugin_menu_remove (AudMenuID id, void (* func) ())
{
    if (current_interface)
        current_interface->plugin_menu_remove (id, func);

    auto is_match = [=] (const MenuItem & item)
        { return item.func == func; };

    menu_items[id].remove_if (is_match, true);
}

EXPORT void aud_ui_show_about_window ()
{
    if (current_interface)
        current_interface->show_about_window ();
}

EXPORT void aud_ui_hide_about_window ()
{
    if (current_interface)
        current_interface->hide_about_window ();
}

EXPORT void aud_ui_show_filebrowser (bool open)
{
    if (current_interface)
        current_interface->show_filebrowser (open);
}

EXPORT void aud_ui_hide_filebrowser ()
{
    if (current_interface)
        current_interface->hide_filebrowser ();
}

EXPORT void aud_ui_show_jump_to_song ()
{
    if (current_interface)
        current_interface->show_jump_to_song ();
}

EXPORT void aud_ui_hide_jump_to_song ()
{
    if (current_interface)
        current_interface->hide_jump_to_song ();
}

EXPORT void aud_ui_show_prefs_window ()
{
    if (current_interface)
        current_interface->show_prefs_window ();
}

EXPORT void aud_ui_hide_prefs_window ()
{
    if (current_interface)
        current_interface->hide_prefs_window ();
}
