/*
 *  soapUI, copyright (C) 2004-2012 smartbear.com 
 *
 *  soapUI is free software; you can redistribute it and/or modify it under the 
 *  terms of version 2.1 of the GNU Lesser General Public License as published by 
 *  the Free Software Foundation.
 *
 *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU Lesser General Public License for more details at gnu.org.
 */

package com.eviware.soapui.support.propertyexpansion.scrollmenu;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.Timer;

import com.eviware.soapui.support.UISupport;

/**
 * JMenu with the scrolling feature.
 */
public class ScrollablePopup extends JPopupMenu implements ScrollableMenuContainer
{
	/** How fast the scrolling will happen. */
	private int scrollSpeed = 20;
	/** Handles the scrolling upwards. */
	private Timer timerUp;
	/** Handles the scrolling downwards. */
	private Timer timerDown;
	/** How many items are visible. */
	private int visibleItems;
	/**
	 * Menuitem's index which is used to control if up and downbutton are visible
	 * or not.
	 */
	private int indexVisible = 0;
	/** Button to scroll menu upwards. */
	private JButton upButton;
	/** Button to scroll menu downwards. */
	private JButton downButton;
	/** Container to hold submenus. */
	private Vector<JMenuItem> subMenus = new Vector<JMenuItem>();
	/** Height of the screen. */
	private double screenHeight;
	/** Height of the menu. */
	private double menuHeight;
	private int headerCount;
	private int footerCount;

	/**
	 * Creates a new ScrollableMenu object with a given name.
	 * <p>
	 * This also instantiates the timers and buttons. After the buttons are
	 * created they are set invisible.
	 * 
	 * @param name
	 *           name to be displayed on the JMenu
	 */
	public ScrollablePopup( String name )
	{
		super( name );

		timerUp = new Timer( scrollSpeed, new ActionListener()
		{
			public void actionPerformed( ActionEvent evt )
			{
				scrollUp();
			}
		} );
		timerDown = new Timer( scrollSpeed, new ActionListener()
		{
			public void actionPerformed( ActionEvent evt )
			{
				scrollDown();
			}
		} );

		screenHeight = 400;
		createButtons();
		hideButtons();
	}

	/**
	 * JMenu's add-method is override to keep track of the added items. If there
	 * are more items that JMenu can display, then the added menuitems will be
	 * invisible. After that downscrolling button will be visible.
	 * 
	 * @param menuItem
	 *           to be added
	 * 
	 * @return added menuitem
	 */
	public JMenuItem add( JMenuItem menuItem )
	{
		add( menuItem, subMenus.size() + headerCount + 1 + ( headerCount == 0 ? 0 : 1 ) );
		subMenus.add( menuItem );

		menuHeight += menuItem.getPreferredSize().getHeight();

		if( menuHeight > screenHeight )
		{
			menuItem.setVisible( false );
			downButton.setVisible( true );
		}
		else
		{
			visibleItems++ ;
		}

		return menuItem;
	}

	public Component add( Component comp )
	{
		if( comp instanceof JMenuItem )
			return add( ( JMenuItem )comp );
		else
			return super.add( comp );
	}

	public void removeAll()
	{
		super.removeAll();

		headerCount = 0;
		footerCount = 0;
		menuHeight = 0;
		indexVisible = 0;
		visibleItems = 0;

		subMenus.clear();

		add( upButton );
		add( downButton );
	}

	/**
	 * When timerUp is started it calls constantly this method to make the JMenu
	 * scroll upwards. When the top of menu is reached then upButton is set
	 * invisible. When scrollUp starts downButton is setVisible.
	 */
	private void scrollUp()
	{
		// closeOpenedSubMenus();

		if( indexVisible == 0 )
		{
			upButton.setVisible( false );

			return;
		}
		else
		{
			indexVisible-- ;
			( ( JComponent )subMenus.get( indexVisible + visibleItems ) ).setVisible( false );
			( ( JComponent )subMenus.get( indexVisible ) ).setVisible( true );
			downButton.setVisible( true );
			if( indexVisible == 0 )
			{
				upButton.setVisible( false );
			}
		}
	}

	/**
	 * When timerDown is started it calls constantly this method to make the
	 * JMenu scroll downwards. When the bottom of menu is reached then downButton
	 * is set invisible. When scrolldown starts upButton is setVisible.
	 */
	private void scrollDown()
	{
		// closeOpenedSubMenus();

		if( ( indexVisible + visibleItems ) == subMenus.size() )
		{
			downButton.setVisible( false );

			return;
		}
		else if( ( indexVisible + visibleItems ) > subMenus.size() )
		{
			return;
		}
		else
		{
			try
			{
				( ( JComponent )subMenus.get( indexVisible ) ).setVisible( false );
				( ( JComponent )subMenus.get( indexVisible + visibleItems ) ).setVisible( true );
				upButton.setVisible( true );
				indexVisible++ ;
				if( ( indexVisible + visibleItems ) == subMenus.size() )
				{
					downButton.setVisible( false );
				}
			}
			catch( Exception eks )
			{
				eks.printStackTrace();
			}
		}
	}

	/**
	 * Creates two button: upButton and downButton.
	 */
	private void createButtons()
	{
		upButton = new JButton( UISupport.createImageIcon( "/up_arrow.gif" ) );

		Dimension d = new Dimension( 100, 20 );
		upButton.setPreferredSize( d );
		upButton.setBorderPainted( false );
		upButton.setFocusPainted( false );
		upButton.setRolloverEnabled( true );

		class Up extends MouseAdapter
		{
			/**
			 * When mouse enters over the upbutton, timerUp starts the scrolling
			 * upwards.
			 * 
			 * @param e
			 *           MouseEvent
			 */
			public void mouseEntered( MouseEvent e )
			{
				try
				{
					timerUp.start();
				}
				catch( Exception ekas )
				{
				}
			}

			/**
			 * When mouse exites the upbutton, timerUp stops.
			 * 
			 * @param e
			 *           MouseEvent
			 */
			public void mouseExited( MouseEvent e )
			{
				try
				{
					timerUp.stop();
				}
				catch( Exception ekas )
				{
				}
			}
		}

		MouseListener scrollUpListener = new Up();
		upButton.addMouseListener( scrollUpListener );

		add( upButton );
		downButton = new JButton( UISupport.createImageIcon( "/down_arrow.gif" ) );
		downButton.setPreferredSize( d );
		downButton.setBorderPainted( false );
		downButton.setFocusPainted( false );

		class Down extends MouseAdapter
		{
			/**
			 * When mouse enters over the downbutton, timerDown starts the
			 * scrolling downwards.
			 * 
			 * @param e
			 *           MouseEvent
			 */
			public void mouseEntered( MouseEvent e )
			{
				try
				{
					timerDown.start();
				}
				catch( Exception ekas )
				{
				}
			}

			/**
			 * When mouse exites the downbutton, timerDown stops.
			 * 
			 * @param e
			 *           MouseEvent
			 */
			public void mouseExited( MouseEvent e )
			{
				try
				{
					timerDown.stop();
				}
				catch( Exception ekas )
				{
				}
			}
		}

		MouseListener scrollDownListener = new Down();
		downButton.addMouseListener( scrollDownListener );
		add( downButton, 1 + subMenus.size() );
	}

	/**
	 * Hides the scrollButtons.
	 */
	public void hideButtons()
	{
		upButton.setVisible( false );
		downButton.setVisible( false );
	}

	public JMenuItem addHeader( JMenuItem header )
	{
		add( header, headerCount );

		if( ++headerCount == 1 )
			add( new JSeparator(), 1 );

		return header;
	}

	public JMenuItem addHeader( Action action )
	{
		return addHeader( new JMenuItem( action ) );
	}

	public JMenuItem addFooter( JMenuItem footer )
	{
		if( footerCount == 0 )
			add( new JSeparator(), subMenus.size() + headerCount + 2 + ( headerCount == 0 ? 0 : 1 ) );

		add( footer, subMenus.size() + headerCount + footerCount + 3 + ( headerCount == 0 ? 0 : 1 ) );
		footerCount++ ;

		return footer;
	}

	public JMenuItem addFooter( Action action )
	{
		return addFooter( new JMenuItem( action ) );
	}
}
