package com.limegroup.gnutella.gui.search;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.util.MissingResourceException;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.ImageIcon;
import javax.swing.Icon;

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

/**
 * This class acts as a wrapper around the "kill" icon displayed in the
 * search tabs.
 */
//2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678|
final class CancelSearchIconProxy implements Icon {
    
    private static final int PLAIN = 0;
    private static final int SELECTED = 1;
    private static final int ARMED = 2;
    
    private static Icon PLAIN_ICON;
    private static Icon SELECTED_ICON;
    private static Icon ARMED_ICON;
	
	/**
	 * The <tt>ImageIcon</tt> for our cancel image.
	 */
	private Icon _cancelIcon;

	/**
	 * The width of the icon in pixels.
	 */
	private int _width;

	/**
	 * The height of the icon in pixels.
	 */
	private int _height;

	/**
	 * The x position of the icon within its tab.
	 */
	private int _x;

	/**
	 * The y position of the icon within its tab.
	 */
	private int _y;
	
	/**
	 * The style of this icon.
	 */
	private final int style;
	
	static CancelSearchIconProxy createPlain() {
	    return new CancelSearchIconProxy(PLAIN);
	}
	
	static CancelSearchIconProxy createSelected() {
	    return new CancelSearchIconProxy(SELECTED);
	}
	
	static CancelSearchIconProxy createArmed() {
	    return new CancelSearchIconProxy(ARMED);
	}

	/**
	 * the constructor loads the image icon and stores the location
	 * and dimensions.
	 */
	CancelSearchIconProxy(int style) {
	    this.style = style;
	    setIcon();
	}
	
	/**
	 * Sets the appropriate icon.
	 */
	private void setIcon() {
        switch(style) {
        case ARMED:
            _cancelIcon = ARMED_ICON;
            break;
        case SELECTED:
            _cancelIcon = SELECTED_ICON;
            break;
        case PLAIN:
            _cancelIcon = PLAIN_ICON;
            break;
        }
		_width  = _cancelIcon.getIconWidth();
		_height = _cancelIcon.getIconHeight();
		_x = 0;
		_y = 0;
	}

	// resets the cached icons for each kind of icon
	static void updateTheme() {
	    if(ThemeSettings.isWindowsTheme() && WindowsXPIcon.isAvailable()) {
	        try {
                PLAIN_ICON = new WindowsXPIcon(PLAIN);
                SELECTED_ICON = new WindowsXPIcon(SELECTED);
                ARMED_ICON = new WindowsXPIcon(ARMED);
                return;
            } catch(IllegalArgumentException iae) {
                // couldn't create image to resize
            } catch(NullPointerException npe) {
                // internal windows plaf error
            }
            // if construction failed, fall through...
        }
        
        PLAIN_ICON = GUIMediator.getThemeImage("kill");
        try {
            SELECTED_ICON = GUIMediator.getThemeImage("kill_on");
        } catch(MissingResourceException mre) {
            SELECTED_ICON = PLAIN_ICON;
        }
        ARMED_ICON = SELECTED_ICON;
    }

    /**
	 * implements Icon interface.
     * Gets the width of the icon.
	 * 
     * @return the width in pixels of this icon
     */
    public int getIconWidth() {
		return _width;
    }

    /**
	 * implements Icon interface.
     * Gets the height of the icon.
	 *
     * @return the height in pixels of this icon
     */
    public int getIconHeight() {
		return _height;
    }

	/**
	 * implements Icon interface.
	 * forwards the call to the proxied Icon object and stores the
	 * x and y coordinates of the icon.
	 */
	public void paintIcon(Component c, Graphics g, int x, int y) {
		_x = x;
		_y = y;
		_cancelIcon.paintIcon(c, g, x, y);
	}

	/**
	 * Determines whether or not a click at the given x, y position
	 * is a "hit" on the kill search icon.
	 * 
	 * @param x the x location of the mouse event
	 *
	 * @param y the y location of the mouse event
	 *
	 * @return <tt>true</tt> if the mouse event occurred within the 
	 *         bounding rectangle of the icon, <tt>false</tt> otherwise.
	 */
	boolean shouldKill(int x, int y) {
        int xMax = _x + _width;
        int yMax = _y + _height;
		if(!((x >= _x) && (x <= xMax))) return false;
		if(!((y >= _y) && (y <= yMax))) return false;
	
		return true;
	}
	
	/**
	 * A delegate for the windows xp icon.
	 *
	 * This is necessary because the windows icon requires it be drawn
	 * from a JButton, and its InternalFrame designates if it's drawn
	 * as armed/disarmed/selected.  It's also always drawn from 0,0
	 * so the graphics object needs to be translated.
	 */
	private static class WindowsXPIcon implements Icon {
    	// cached windows icon, and a marker null value so we don't
    	// needlessy try to recreate it each time.
    	private static Icon _windowsCloseIcon;
    	private static final Icon NULL = new ImageIcon();
    	
    	/**
    	 * Determines if the Windows icon is available and caches it.
    	 */
    	static boolean isAvailable() {
    	    if(!CommonUtils.isJava142OrLater() ||
    	       !CommonUtils.isWindowsXP() ||
               !Boolean.TRUE.equals(
                   Toolkit.getDefaultToolkit().getDesktopProperty(
                       "win.xpstyle.themeActive")) ||
               System.getProperty("swing.noxp") != null)
                 return false;
    	    
    	    if(_windowsCloseIcon == NULL)
    	        return false;
    	    if(_windowsCloseIcon != null)
    	        return true;
    	        
    	    try {
    	        Class c = Class.forName("com.sun.java.swing.plaf.windows.WindowsIconFactory");
    	        Method m = c.getDeclaredMethod("createFrameCloseIcon", new Class[0]);
    	        _windowsCloseIcon = (Icon)m.invoke(c, new Object[0]);
    	        if(_windowsCloseIcon.getIconHeight() == 0 
    	         || _windowsCloseIcon.getIconWidth() == 0) {
        	        _windowsCloseIcon = NULL;
        	        return false;
                } else {
                    return true;
                }   
            } catch(ClassNotFoundException cfnfe) {
            } catch(IllegalAccessException iae) {
            } catch(ExceptionInInitializerError eiie) {
            } catch(SecurityException se) {
            } catch(ClassCastException cce) {
            } catch(NoSuchMethodException nsme) {
            } catch(InvocationTargetException ite) {
            }
            
            _windowsCloseIcon = NULL;
            return false;
        }
        
        /**
         * The button to use when drawing the icon.
         */
	    private Component component;
	    
	    /**
	     * The resized version of the icon.
	     */
    	private Icon _resizedIcon;
	    
	    /**
	     * Constructs a new icon with the given button.
	     */
	    WindowsXPIcon(final int style) {
	        JButton button = new JButton();
	        button.getModel().setArmed(false);
	        button.getModel().setPressed(false);
	        button.getModel().setRollover(style == ARMED);
	        button.getModel().setEnabled(true);
	        JInternalFrame frame = new JInternalFrame() {
	            public boolean isSelected() { return style != PLAIN; }
	        };
	        frame.getContentPane().add(button);
	        component = button;
	        
	        // set the correct size.
	        Icon icon = ImageManipulator.resize(this, 14, 14);
	        _resizedIcon = icon;
	        
	        // component not needed any more.
	        component = null;
        }
        
        // icon methods.
        public int getIconHeight() {
            if(_resizedIcon != null)
                return _resizedIcon.getIconHeight();
            else
                return _windowsCloseIcon.getIconHeight();
        }
        
        public int getIconWidth() {
            if(_resizedIcon != null)
                return _resizedIcon.getIconWidth();
            else
                return _windowsCloseIcon.getIconWidth();
        }
        
        /**
         * Translates the graphics component prior to drawing, since the
         * icon always draws at 0,0.
         */
        public void paintIcon(Component c, Graphics g, int x, int y) {
            if(_resizedIcon != null) {
                _resizedIcon.paintIcon(c, g, x, y);
            } else {
                g.translate(x, y);
                _windowsCloseIcon.paintIcon(component, g, 0, 0);
                g.translate(-x, -y);
            }
        }
    }   
	    
}

