package com.limegroup.gnutella.gui.menu;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FilenameFilter;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

import javax.swing.ButtonGroup;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.UIManager;

import com.limegroup.gnutella.gui.GUIMediator;
import com.limegroup.gnutella.gui.themes.ThemeMediator;
import com.limegroup.gnutella.gui.themes.ThemeSettings;
import com.limegroup.gnutella.util.CommonUtils;

/**
 * The menu to be used for themes.
 */
final class ThemeMenu extends AbstractMenu {
    
    /**
     * The client property to use for theme changing items.
     */
    private static final String THEME_PROPERTY = "THEME_NAME";
    
    /**
     * The client property to use for theme changing when using 'other' L&Fs.
     */
    private static final String THEME_CLASSNAME = "THEME_CLASSNAME";
    
    /**
     * The listener for changing the theme.
     */
    private static final ActionListener THEME_CHANGER =
        new ThemeChangeListener();
    
    /**
     * The ButtonGroup to store the theme options in.
     */
    private static final ButtonGroup GROUP = new ButtonGroup();
    
    /**
     * Constructs the menu.
     */
    ThemeMenu(String key) {
        super(key);
        
        addMenuItem("VIEW_THEMES_GET_MORE", new GetThemesListener());
        addMenuItem("VIEW_THEMES_REFRESH", new RefreshThemesListener());
        
        JMenuItem def = addMenuItem("VIEW_THEMES_USE_DEFAULT", THEME_CHANGER);            
        final Object defaultVal = ThemeSettings.THEME_DEFAULT.getAbsolutePath();
        def.putClientProperty(THEME_PROPERTY, defaultVal);
        
        // Add a listener to set the new theme as selected.
        def.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                setSelection(defaultVal);
            }
        });
        addSeparator();
        addThemeItems();
    }
    
    /**
     * Sets the default theme.
     */
    private static void setSelection(Object value) {
        Enumeration items = GROUP.getElements();
        while(items.hasMoreElements()) {
            JMenuItem item = (JMenuItem)items.nextElement();
            if(value.equals(item.getClientProperty(THEME_PROPERTY))) {
                item.setSelected(true);
                break;
            }
        }
    }        
    
    /**
     * Scans through the theme directory for .lwtp files & adds them
     * as menu items to the menu.
     */ 
    private void addThemeItems() {
        File themeDir = ThemeSettings.THEME_DIR_FILE;
        if(!themeDir.exists()) return;
       
        List allThemes = new LinkedList(Arrays.asList(themeDir.list(new ThemeFileFilter())));
        addInstalledLFs(allThemes);
        Collections.sort(allThemes, new ThemeComparator());
        
        if(allThemes.isEmpty())
            return;
            
        String otherClassName = ThemeSettings.getOtherLF();
        
        for(Iterator i = allThemes.iterator(); i.hasNext(); ) {
            Object next = i.next();
            File themeFile;
            JMenuItem theme;
            
            if(next instanceof String) {
                themeFile = new File(themeDir, (String)next);
                theme = new JRadioButtonMenuItem(ThemeSettings.formatName(themeFile.getName()));
                if( themeFile.equals(ThemeSettings.THEME_FILE.getValue()) )
                    theme.setSelected(true);
            } else {
                themeFile = new File(themeDir, ThemeSettings.OTHER_THEME_NAME);
                UIManager.LookAndFeelInfo lfi = (UIManager.LookAndFeelInfo)next;
                theme = new JRadioButtonMenuItem(lfi.getName());
                if( themeFile.equals(ThemeSettings.THEME_FILE.getValue()) &&
                    otherClassName != null && lfi.getClassName().equals(otherClassName) )
                    theme.setSelected(true);
                theme.putClientProperty(THEME_CLASSNAME, lfi.getClassName());
            }
                
            theme.setFont(AbstractMenu.FONT);
            GROUP.add(theme);
            theme.addActionListener(THEME_CHANGER);
            theme.putClientProperty(THEME_PROPERTY, themeFile.getAbsolutePath());
            MENU.add(theme);
        }
    }
    
    /**
     * Removes all items in the group from the menu.  Used for refreshing.
     */
    private void removeThemeItems() {
        Enumeration items = GROUP.getElements();
        List removed = new LinkedList();
        while(items.hasMoreElements()) {
            JMenuItem item = (JMenuItem)items.nextElement();
            MENU.remove(item);
            removed.add(item);
        }
        
        for(Iterator itr = removed.iterator(); itr.hasNext();)
            GROUP.remove((JMenuItem)itr.next());
    }
    
    /**
     * Opens the themes page in the default browser, displaying
     * an error message if the browser could not be launched 
     * successfully.
     */
    private static class GetThemesListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
    		String url = "http://www.limewire.com/skins2";
    	    GUIMediator.openURL(url);
    	}
    }
    
    /**
     * Refreshes the theme menu options to those on the disk.
     */
    private class RefreshThemesListener implements ActionListener {
    	public void actionPerformed(ActionEvent e) {
            removeThemeItems();
            addThemeItems();
    	}
    }    
    
    /**
     * ActionListener to change the theme based on the client property.
     */
    protected static class ThemeChangeListener implements ActionListener {    
        public void actionPerformed(ActionEvent e) {
            JMenuItem item = (JMenuItem)e.getSource();
            String themePath = (String)item.getClientProperty(THEME_PROPERTY);
            String className = (String)item.getClientProperty(THEME_CLASSNAME);
    	    ThemeMediator.changeTheme(new File(themePath), className);
        }
    }
    
    /**
     * Simple class to sort the theme lists.
     */
    private static class ThemeComparator implements Comparator {
        public int compare(Object a, Object b) {
            String name1, name2;
            if(a instanceof String)
                name1 = ThemeSettings.formatName((String)a);
            else
                name1 = ((UIManager.LookAndFeelInfo)a).getName();
                
            if(b instanceof String)
                name2 = ThemeSettings.formatName((String)b);
            else
                name2 = ((UIManager.LookAndFeelInfo)b).getName();

            return name1.compareTo(name2);
        }
    }
    
    /**
     * Adds installed LFs to the list.
     */
    private static void addInstalledLFs(List themes) {
        UIManager.LookAndFeelInfo[] lfs = UIManager.getInstalledLookAndFeels();
        if(lfs == null)
            return;
            
        for(int i = 0; i < lfs.length; i++) {
            UIManager.LookAndFeelInfo l = lfs[i];
            if(l.getClassName().equals("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"))
                continue;
            if(l.getClassName().startsWith("apple"))
                continue;
            if(l.getClassName().equals("com.sun.java.swing.plaf.gtk.GTKLookAndFeel") &&
               CommonUtils.isLinux() && CommonUtils.isJava15OrLater())
                continue;
            if(l.getClassName().equals("com.sun.java.swing.plaf.motif.MotifLookAndFeel"))
                continue;
                
            themes.add(l);
        }
    }
    
    /**
     * <tt>FileNameFilter</tt> class for only displaying theme file types.
     */
    public static class ThemeFileFilter implements FilenameFilter {
        public boolean accept(File dir, String name) {
            // don't allow anything that isn't a theme file
            if(!name.endsWith(ThemeSettings.EXTENSION))
                return false;
                
            // if this is one of the old 'default_X' themes
            // we used to ship with, ignore it.
            if(name.startsWith("default_"))
                return false;
                
            // don't allow the 'other' theme to show.
            if(name.equals(ThemeSettings.OTHER_THEME_NAME))
                return false;
                
            // only allow the osx theme if we're on osx.
            if(!CommonUtils.isMacOSX() && 
              name.equals(ThemeSettings.PINSTRIPES_OSX_THEME_NAME))
                return false;
                
            // only allow the brushed metal theme if we're on
            // osx with 10.3
            if(name.equals(ThemeSettings.BRUSHED_METAL_OSX_THEME_NAME) &&
                    CommonUtils.isJaguarOrAbove())
                return false;
                
            // only allow the windows theme if we're on windows.
            if(!CommonUtils.isWindows() &&
              name.equals(ThemeSettings.WINDOWS_LAF_THEME_NAME))
                return false;
            
            // only show pro theme if we're on pro.
            if(!CommonUtils.isPro() &&
               name.equals(ThemeSettings.PRO_THEME_NAME))
                return false;
                
            // only show GTK theme on linux with 1.5  
            if(name.equals(ThemeSettings.GTK_LAF_THEME_NAME) &&   
              (!CommonUtils.isLinux() ||  
               !CommonUtils.isJava15OrLater()))  
                return false;  

            // everything's okay -- allow it.                
            return true;
        }
    }
    
}
