/*
 * Created on Jan 12, 2004
 *
 */
package LumiSoft.UI.Controls;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.event.*;
import java.util.ArrayList;

import LumiSoft.UI.Controls.Paint;

/**
 * Menubar control.
 */
public class WMenuBar extends WControl implements WContextMenuListener
{	
	private EventListenerList    m_pListners         = null;
	private ArrayList<WMenuItem> m_pItems            = null;
	private WMenuItem            m_pHotItem          = null;
	private WContextMenu         m_pActiveSubMenu    = null;
	private Point                m_MousePos          = new Point(-1,-1);
	private BufferedImage        m_pPaintBuffer      = null;
	private Graphics2D           m_pPaintGraphics    = null;
	
	/**
	 * Default constructor.
	 *
	 */
	public WMenuBar()
	{
		super();
		
		m_pListners = new EventListenerList();
		m_pItems    = new ArrayList<WMenuItem>();
		
		this.setFocusable(true);
		this.addFocusListener(new FocusAdapter(){
			public void focusGained(FocusEvent e){
				OnFocusGained(e);
			}
			
			public void focusLost(FocusEvent e){
				OnFocusLost(e);
			}
		});
		this.addMouseListener(new MouseAdapter(){			
			public void mouseReleased(MouseEvent e){
				OnMouseReleased(e);
			}
			
			public void mouseExited(MouseEvent e){
				OnMouseExited(e);
			}
		});
		this.addMouseMotionListener(new MouseMotionAdapter(){
			public void mouseMoved(MouseEvent e){
				OnMouseMoved(e);
			}
		});
	}
	
	/**
	 * Clean up any resources being used.
	 */
	public void Dispose()
	{
		super.Dispose();
	}
	
	
	/**
	 * Is called when ViewStyle has changed.
	 *
	 */
	protected void OnViewStyleChaned()
	{
		if(m_pPaintGraphics != null){
			m_pPaintGraphics.dispose();
			m_pPaintGraphics = null;
		}
		m_pPaintBuffer = null;
		
		repaint();
	}
	
	/**
	 * Is called when UI language changed.
	 *
	 */
	protected void OnLanguageChanged()
	{
		m_pPaintBuffer = null;
		repaint();		
	}
	
	/**
	 * Is called by system when control need to repaint.
	 */
	public void paintComponent(Graphics g)
	{
		super.paintComponent(g);
		
		// All painting is done to offscreen image, we just need to paint
		// offscreen image to Graphics.
		
		// This is first time paint or size changed, we need to create new offscreen image
		if(m_pPaintBuffer == null || (m_pPaintBuffer.getWidth() != this.getBounds().width || m_pPaintBuffer.getHeight() != this.getBounds().height)){
			m_pPaintBuffer = (BufferedImage)this.createImage(this.getBounds().width,this.getBounds().height);
		
			// Release old paint graphics, if exists
			if(m_pPaintGraphics != null){
				m_pPaintGraphics.dispose();
				m_pPaintGraphics = null;
			}
						
			Paint(GetPaintGraphics());
		}
		
		// Draw offscreen image
		g.drawImage(m_pPaintBuffer,0,0,this);
	}
	
	/**
	 * Gets Graphics object what must be used for painting.
	 * @return
	 */
	protected Graphics2D GetPaintGraphics()
	{
		if(m_pPaintBuffer != null && m_pPaintGraphics == null){
			m_pPaintGraphics = m_pPaintBuffer.createGraphics();
		}

		return m_pPaintGraphics;
	}
	
	/**
	 * Paints menubar to specified Graphics object.
	 * @param g
	 */
	private void Paint(Graphics2D g)
	{
		if(g == null){
			return;
		}
		
		// Fill background
		g.setColor(getViewStyle().getFormColor());
		g.fillRect(0,0,getWidth(),getHeight());
		
		Calculate((Graphics2D)g);
		
		for(WMenuItem item : m_pItems){			
			boolean hot = item.getBounds().contains(m_MousePos);
			DrawItem((Graphics2D)g,item,hot,(hot && this.hasFocus()) || (item.getSubMenu() != null && item.getSubMenu() == m_pActiveSubMenu));
		}
	}
	
	/**
	 * Draws menubar item to specified Graphics object.
	 * @param g Graphics to draw.
	 * @param item Item to draw.
	 * @param hot Specifies if mouse in that item.
	 * @param open Specifies if sub menu is open.
	 */
	private void DrawItem(Graphics2D g,WMenuItem item,boolean hot,boolean open)
	{	
		if(g == null){
			return;		
		}
		if(item == null){
			return;
		}
		
		//---- Draw backround --------------------------//
		if(!item.getEnabled() || !this.isEnabled()){
			g.setColor(getViewStyle().getFormColor());
			g.fill(item.getBounds());
		}
	//	else if(open){
	//		g.setColor(getViewStyle().getBorderColor());
		//	g.fill(item.getBounds());
			
	//		g.setColor(getViewStyle().getBorderColor());
		//	g.drawRect(item.getBounds().x,item.getBounds().y,item.getBounds().width - 1,item.getBounds().height - 1);
	//	}
		else if(hot || open){			
			g.setColor(ViewStyle.GetColorFromRgb(182,193,214));
			g.fill(item.getBounds());
			
			g.setColor(ViewStyle.GetColorFromRgb(0,0,0));
			g.drawRect(item.getBounds().x,item.getBounds().y,item.getBounds().width - 1,item.getBounds().height - 1);			
		}
		else{
			g.setColor(getViewStyle().getFormColor());
			g.fill(item.getBounds());
		}
		//------------------------------------------------//
		
		//---- Draw text -----//
		if(!item.getEnabled() || !this.isEnabled()){
			g.setColor(Color.GRAY);
		}
		else{
			g.setColor(Color.BLACK);
		}
		Paint.DrawText(g,this.getFont(),item.getText(),item.getBounds(),HorizontalAlignment.Center);
		//--------------------//
	}
	
		
	/**
	 * Is called when focus has gained.
	 * @param e
	 */	
	private void OnFocusGained(FocusEvent e)
	{
	//	this.repaint();
	}
	
	/**
	 * Is called when focus has lost.
	 * @param e
	 */
	private void OnFocusLost(FocusEvent e)
	{
		// Close open submenu
	//	if(m_pActiveSubMenu != null){
	//		m_pActiveSubMenu.Hide();
	//		m_pActiveSubMenu = null;
	//	}
		
		// Deselect hot item
	//	if(m_pHotItem != null){
	//		DrawItem((Graphics2D)this.getGraphics(),m_pHotItem,m_pHotItem.getBounds().contains(m_MousePos),m_pHotItem.getSubMenu() == m_pActiveSubMenu);			
	//	}
	}
	
	
	/**
	 * Is called when mouse has released.
	 * @param e
	 */
	private void OnMouseReleased(MouseEvent e)
	{		
		if(!this.hasFocus()){
			this.grabFocus();
			
			if(m_pHotItem != null && m_pHotItem.getSubMenu() != null){
				m_pActiveSubMenu = m_pHotItem.getSubMenu();
				m_pActiveSubMenu.addContextMenuListener(this);
				m_pActiveSubMenu.setWText(getWText());
				m_pActiveSubMenu.Show(this.getLocationOnScreen().x + m_pHotItem.getBounds().x,this.getLocationOnScreen().y + this.getHeight());
				
				DrawItem(GetPaintGraphics(),m_pHotItem,true,true);
				repaint();
			}
		}
	}
			
	/**
	 * Is called when mouse cursor has exited this control.
	 * @param e
	 */
	private void OnMouseExited(MouseEvent e)
	{
		m_MousePos = new Point(-1,-1);
		
		// Deselect hot item
		if(m_pHotItem != null){
			DrawItem(GetPaintGraphics(),m_pHotItem,false,m_pHotItem.getSubMenu() == m_pActiveSubMenu);
			repaint();
			
			if(m_pHotItem.getSubMenu() != m_pActiveSubMenu){
				m_pHotItem = null;
			}
		}
	}
	
	/**
	 * Is called when mouse has moved.
	 * @param e
	 */
	private void OnMouseMoved(MouseEvent e)
	{
		if(!this.isEnabled()){
			return;
		}
		
		m_MousePos = e.getPoint();
		
		WMenuItem inMouseItem = getInMouseItem();
		if(inMouseItem != m_pHotItem){
		
			// Close open submenu
			if(m_pActiveSubMenu != null){
				m_pActiveSubMenu.removeContextMenuListener(this);
				m_pActiveSubMenu.Hide();
				m_pActiveSubMenu = null;
			}
			
			//--- Redraw ------------------------------------------------//
			if(m_pHotItem != null){
				DrawItem(GetPaintGraphics(),m_pHotItem,false,false);
				repaint();
			}
			if(inMouseItem != null && !inMouseItem.getSeparator()){
				DrawItem(GetPaintGraphics(),inMouseItem,true,this.hasFocus());
				repaint();
			}
			//-----------------------------------------------------------//
								
			m_pHotItem = inMouseItem;
									
			// Show item context menu
			if(this.hasFocus() && m_pHotItem != null && m_pHotItem.getSubMenu() != null){				
				m_pActiveSubMenu = m_pHotItem.getSubMenu();
				m_pActiveSubMenu.addContextMenuListener(this);
				m_pActiveSubMenu.setWText(getWText());
				m_pActiveSubMenu.Show(this.getLocationOnScreen().x + m_pHotItem.getBounds().x,this.getLocationOnScreen().y + this.getHeight());
			}
		}	
	}
	
	public void ContextMenuClosed()
	{	
		if(m_pActiveSubMenu != null){
			m_pActiveSubMenu.removeContextMenuListener(this);
			m_pActiveSubMenu = null;
		}
		
		m_pHotItem = null;		
		
		Paint(GetPaintGraphics());
		this.repaint();	
	}
	
	public void MenuItemSelected(WMenuItem item)
	{					
		// Raise event
		OnMenuItemSelected(item);		
	}
	
	
	/**
	 * Add new menu tiem to items collection.
	 * @param text Menu item text.
	 * @return Returns added menu item.
	 */
	public WMenuItem Add(String text)
	{
		return Add(text,"");
	}
	/**
	 * Add new menu tiem to items collection.
	 * @param text Menu item text.
	 * @param textID Text ID.
	 * @return Returns added menu item.
	 */
	public WMenuItem Add(String text,String textID)
	{
		return Add(text,"",null);
	}
	/**
	 * Add new menu tiem to items collection.
	 * @param text Menu item text.
	 * @param textID Text ID.
	 * @param tag User data.
	 * @return Returns added menu item.
	 */
	public WMenuItem Add(String text,String textID,Object tag)
	{
		WMenuItem item = new WMenuItem(this);
		item.setText(text);
		item.setTextID(textID);
		item.setTag(tag);
		m_pItems.add(item);
		
		return item;
	}
	
	/**
	 * Gets menu item from specified index.
	 * @param value
	 * @return
	 */
	public WMenuItem get(int value)
	{
		return (WMenuItem)m_pItems.get(value);
	}
	
	/**
	 * Calculates draw info.
	 * @param g
	 */
	private void Calculate(Graphics2D g)
	{
		int width = 0;		
		for(WMenuItem item : m_pItems){
			int itemWidth = Paint.CalcTextWidth(g,this.getFont(),item.getText()) + 10;
			item.setBounds(new Rectangle(width,1,itemWidth + 1,this.getHeight() - 1));
			width += itemWidth;			
		}		
	}
	
	
	/**
	 * Gets menu item what is currently in mouse cursor.
	 * @return
	 */
	private WMenuItem getInMouseItem()
	{
		for(WMenuItem item : m_pItems){			
			if(item.getBounds() != null && item.getBounds().contains(m_MousePos)){
				return item;
			}
		}
		
		return null;
	}
		
	
	/**
	 * Gets if menubar is enabled.
	 * @return
	 */
	public boolean getEnabled()
	{
		return this.isEnabled();
	}
	/**
	 * Enables or disables menubar. 
	 */
	public void setEnabled(boolean value)
	{
		super.setEnabled(value);
		
		if(m_pActiveSubMenu != null){
			m_pActiveSubMenu.Hide();
			m_pActiveSubMenu = null;
		}
		
		Paint(GetPaintGraphics());
		this.repaint();		
	}

	
	/**
	 * Adds the specified action listener to receive action events
	 * from this button.
	 * @param l the action listener
	 */ 
	public void addMenuListener(WContextMenuListener l) 
	{
		m_pListners.add(WContextMenuListener.class,l);
	}

	/**
	 * Removes the specified action listener so it no longer receives
	 * action events from this button.
	 * @param l the action listener
	 */ 
	public void removeMenuIListener(WContextMenuListener l) 
	{
		m_pListners.remove(WContextMenuListener.class,l);
	}
	
	
	protected void OnMenuItemSelected(WMenuItem item)
	{
		Object[] listeners = m_pListners.getListenerList();
		// loop through each listener and pass on the event if needed		
		for(int i=0;i < listeners.length;i++){
			if(listeners[i] == WContextMenuListener.class){
				// pass the event to the listeners event dispatch method
				((WContextMenuListener)listeners[i+1]).MenuItemSelected(item);
			}            
		}
	}
}
