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

import java.util.HashMap;
import java.util.Properties;



import org.flexdock.plaf.Configurator;
import org.flexdock.plaf.IFlexViewComponentUI;
import org.flexdock.plaf.PlafManager;
import org.flexdock.plaf.PropertySet;
import org.flexdock.plaf.XMLConstants;
import org.w3c.dom.Element;

/**
 * @author Christopher Butler
 */
public class UIFactory implements XMLConstants {

    public static final String DEFAULT = "default";
    public static final String THEME_KEY = "theme";
    public static final String VIEW_KEY = "view-ui";
    public static final String TITLEBAR_KEY = "titlebar-ui";
    public static final String BUTTON_KEY = "button-ui";
    private static final HashMap VIEW_UI_CACHE = new HashMap();
    private static final HashMap TITLEBAR_UI_CACHE = new HashMap();
    private static final HashMap BUTTON_UI_CACHE = new HashMap();
    private static final HashMap THEME_UI_CACHE = new HashMap();

    public static ViewUI getViewUI(String name) {
        return (ViewUI)getUI(name, VIEW_UI_CACHE, VIEW_KEY, ViewUI.class);
    }

    public static ViewUI getViewUI(Properties p) {
        return (ViewUI)getUI(p, VIEW_UI_CACHE, VIEW_KEY, ViewUI.class);
    }

    public static TitlebarUI getTitlebarUI(String name) {
        return (TitlebarUI)getUI(name, TITLEBAR_UI_CACHE, TITLEBAR_KEY, TitlebarUI.class);
    }

    public static TitlebarUI getTitlebarUI(Properties p) {
        return (TitlebarUI)getUI(p, TITLEBAR_UI_CACHE, TITLEBAR_KEY, TitlebarUI.class);
    }

    public static ButtonUI getButtonUI(String name) {
        return (ButtonUI)getUI(name, BUTTON_UI_CACHE, BUTTON_KEY, ButtonUI.class);
    }

    public static ButtonUI getButtonUI(Properties p) {
        return (ButtonUI)getUI(p, BUTTON_UI_CACHE, BUTTON_KEY, ButtonUI.class);
    }



    public static Theme getTheme(String name) {
        if(Configurator.isNull(name))
            return null;

        Theme theme = (Theme)THEME_UI_CACHE.get(name);
        if(theme==null) {
            theme = loadTheme(name);
            if(theme!=null) {
                synchronized(THEME_UI_CACHE) {
                    THEME_UI_CACHE.put(name, theme);
                }
            }
        }
        return theme;
    }

    private static IFlexViewComponentUI getUI(Properties p, HashMap cache, String tagName, Class rootClass) {
        if(p==null || !p.containsKey(tagName))
            return null;

        String name = p.getProperty(tagName);
        return getUI(name, cache, tagName, rootClass);
    }

    private static IFlexViewComponentUI getUI(String name, HashMap cache, String tagName, Class rootClass) {
        if(Configurator.isNull(name))
            return null;

        IFlexViewComponentUI ui = (IFlexViewComponentUI)cache.get(name);
        if(ui==null) {
            ui = loadUI(name, tagName, rootClass);
            if(ui!=null) {
                synchronized(cache) {
                    cache.put(name, ui);
                }
            }
        }
        return ui;
    }

    private static IFlexViewComponentUI loadUI(String name, String tagName, Class rootClass) {
        PropertySet properties = Configurator.getProperties(name, tagName);
        String classname = properties.getString(CLASSNAME_KEY);
        Class implClass = loadUIClass(classname, rootClass);

        try {
            IFlexViewComponentUI ui = (IFlexViewComponentUI)implClass.newInstance();
            ui.setCreationParameters(properties);
            return ui;
        } catch(Exception e) {
            // we use public, no-argument constructors, so if this happens, we
            // have a configuration error.
            System.err.println("Exception: " +e.getMessage());
            e.printStackTrace();
            return null;
        }
    }

    private static Class loadUIClass(String classname, Class rootClass) {
        if(Configurator.isNull(classname))
            return rootClass;

        Class implClass = null;
        try {
            implClass = Class.forName(classname);
            if(!rootClass.isAssignableFrom(implClass)) {
                implClass = null;
            }
        } catch(ClassNotFoundException e) {
            System.err.println("Exception: " + e.getMessage());
            implClass = null;
        }
        return implClass==null? rootClass: implClass;
    }

    private static Theme loadTheme(String themeName) {
        HashMap map = Configurator.getNamedElementsByTagName(THEME_KEY);
        if(map==null)
            return null;
        return loadTheme(themeName, map);
    }

    private static Theme loadTheme(String themeName, HashMap cache) {
        Element themeElem = (Element)cache.get(themeName);
        if(themeElem==null)
            return null;

        // if we're an indirect reference to a different theme, then return that theme
        String redirect = themeElem.getAttribute(REFERENCE_KEY);
        if(!Configurator.isNull(redirect))
            return loadTheme(redirect, cache);

        // if we're a child of another theme, then load the parent and
        // add our properties afterward
        String parentName = themeElem.getAttribute(EXTENDS_KEY);
        Theme theme = Configurator.isNull(parentName)? new Theme(): loadTheme(parentName, cache);
        if(theme==null)
            theme = new Theme();

        String name = themeElem.getAttribute(NAME_KEY);
        String desc = themeElem.getAttribute(DESC_KEY);
        String view = themeElem.getAttribute(VIEW_KEY);

        theme.setName(name);
        theme.setDescription(desc);

        ViewUI viewUI = Configurator.isNull(view)? getViewUI(DEFAULT): getViewUI(view);
        TitlebarUI titlebarUI = viewUI==null? getTitlebarUI(DEFAULT): getTitlebarUI(viewUI.getPreferredTitlebarUI());
        ButtonUI buttonUI = titlebarUI==null? getButtonUI(DEFAULT): getButtonUI(titlebarUI.getPreferredButtonUI());

        theme.setViewUI(viewUI);
        theme.setTitlebarUI(titlebarUI);
        theme.setButtonUI(buttonUI);

        return theme;
    }

    public static Theme createTheme(Properties p) {
        if(p==null)
            return null;

        Theme base = getTheme(PlafManager.getSystemThemeName());

        ViewUI view = getViewUI(p);
        if(view==null)
            view = base.getViewUI();
        if(view==null)
            view = getViewUI(DEFAULT);

        TitlebarUI titlebar = getTitlebarUI(p);
        if(titlebar==null)
            titlebar = getTitlebarUI(view.getPreferredTitlebarUI());
        if(titlebar==null)
            titlebar = base.getTitlebarUI();
        if(titlebar==null)
            titlebar = getTitlebarUI(DEFAULT);

        ButtonUI button = getButtonUI(p);
        if(button==null)
            button = getButtonUI(titlebar.getPreferredButtonUI());
        if(button==null)
            button = base.getButtonUI();
        if(button==null)
            button = getButtonUI(DEFAULT);

        Theme theme = new Theme();
        theme.setName(p.getProperty(NAME_KEY, "custom"));
        theme.setDescription(p.getProperty(DESC_KEY, "Custom Theme"));
        theme.setViewUI(view);
        theme.setTitlebarUI(titlebar);
        theme.setButtonUI(button);
        return theme;
    }
}
