/*
 * Created on Mar 1, 2005
 */
package org.flexdock.plaf;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Hashtable;
import java.util.Properties;

import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;

import org.flexdock.plaf.mappings.PlafMappingFactory;
import org.flexdock.plaf.theme.Theme;
import org.flexdock.plaf.theme.UIFactory;
import org.flexdock.util.RootWindow;
import org.flexdock.view.Button;
import org.flexdock.view.Titlebar;
import org.flexdock.view.View;

/**
 * @author Christopher Butler
 */
public class PlafManager {
    private static final String PREFERRED_THEME_KEY = "preferred.theme";

    private static final String UI_CHANGE_EVENT = "lookAndFeel";

    private static final Hashtable UI_DEFAULTS = new Hashtable();

    private static final Hashtable CUSTOM_THEMES = new Hashtable();

    static {
        initialize();
    }

    private static void initialize() {
        installPreferredTheme();
        // install an updater so we can keep up with changes in the installed
        // plaf
        UIManager.addPropertyChangeListener(new UiUpdater());
    }

    public static String getSystemThemeName() {
        return PlafMappingFactory.getInstalledPlafReference();
    }

    public static void installSystemTheme() {
        setPreferredTheme(getSystemThemeName());
    }

    public static void setPreferredTheme(Properties p) {
        String themeName = p == null ? null : p
                .getProperty(XMLConstants.NAME_KEY);
        if (themeName == null)
            throw new IllegalArgumentException(
                    "Unable to find property 'name' in the supplied data set.");

        Theme theme = addCustomTheme(themeName, p);
        if (theme != null)
            setPreferredTheme(themeName, true);
    }

    public static void setPreferredTheme(String themeName) {
        setPreferredTheme(themeName, false);
    }

    public static void setPreferredTheme(String themeName, boolean reload) {
        String oldPref = (String) UI_DEFAULTS.get(PREFERRED_THEME_KEY);

        if (Configurator.isNull(themeName))
            UI_DEFAULTS.remove(PREFERRED_THEME_KEY);
        else
            UI_DEFAULTS.put(PREFERRED_THEME_KEY, themeName);

        String newPref = (String) UI_DEFAULTS.get(PREFERRED_THEME_KEY);

        // this will handle the case where we switch from null to something
        // else,
        // vice versa, or a new string value
        boolean themeChanged = oldPref != newPref
                && (oldPref == null || newPref == null || !oldPref
                        .equals(newPref));
        if (reload || themeChanged)
            installPreferredTheme();
    }

    public static void installPreferredTheme() {
        Theme theme = getPreferredTheme();

        UI_DEFAULTS.clear();
        setProperty(View.class, theme.getViewUI());
        setProperty(Titlebar.class, theme.getTitlebarUI());
        setProperty(Button.class, theme.getButtonUI());

        RootWindow[] windows = RootWindow.getVisibleWindows();
        for (int i = 0; i < windows.length; i++)
            windows[i].updateComponentTreeUI();
    }

    private static Theme getPreferredTheme() {
        Theme theme = null;
        String themeName = (String) UI_DEFAULTS.get(PREFERRED_THEME_KEY);
        if (themeName != null) {
            theme = (Theme) CUSTOM_THEMES.get(themeName);
            if (theme == null)
                theme = UIFactory.getTheme(themeName);
        }
        if (theme == null)
            theme = UIFactory.getTheme(getSystemThemeName());
        if (theme == null)
            theme = UIFactory.getTheme(UIFactory.DEFAULT);
        return theme;
    }

    public static Theme addCustomTheme(String themeName, Properties p) {
        return loadCustomTheme(themeName, p, false);
    }

    public static Theme setCustomTheme(String themeName, Properties p) {
        return loadCustomTheme(themeName, p, true);
    }

    public static Theme loadCustomTheme(String themeName, Properties p,
            boolean exclusive) {
        if (Configurator.isNull(themeName) || p == null)
            return null;

        Theme theme = UIFactory.createTheme(p);
        if (theme != null) {
            theme.setName(themeName);
            if (exclusive)
                CUSTOM_THEMES.clear();
            CUSTOM_THEMES.put(themeName, theme);
        }
        return theme;
    }

    public static Theme removeCustomTheme(String themeName) {
        return Configurator.isNull(themeName) ? null : (Theme) CUSTOM_THEMES
                .remove(themeName);
    }

    private static void setProperty(Object key, Object value) {
        if (key != null && value != null)
            UI_DEFAULTS.put(key, value);
    }

    /**
     * Returns the appropriate {@code ComponentUI} implementation for
     * {@code target}. In case the component is a member of the installed look
     * and feel, this method first queries {@code UIManager.getUI(target)}
     * before attempting to resolve it locally.
     * 
     * @param target
     *            the {@code JComponent} to return the {@code ComponentUI} for
     * @return the {@code ComponentUI} object for {@code target}
     * @throws NullPointerException
     *             if {@code target} is {@code null}
     * @see UIManager#getUI
     */
    public static ComponentUI getUI(JComponent target) {
        ComponentUI ui = /*UIManager.getUI(target);

        if (ui == null) {
            ui = */(ComponentUI) UI_DEFAULTS.get(target.getClass());
//        }

        return ui;
    }

    private static class UiUpdater implements PropertyChangeListener {
        public void propertyChange(PropertyChangeEvent evt) {
            if (UI_CHANGE_EVENT.equals(evt.getPropertyName())
                    && evt.getOldValue() != evt.getNewValue()) {
                installPreferredTheme();
            }
        }
    }
}
