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

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;

import javax.swing.JDialog;
import javax.swing.event.*;
import java.util.*;
import LumiSoft.Data.*;
import LumiSoft.UI.Controls.*;

/**
 * Grid control.
 */
public class WGridControl extends WControl
{
	private   EventListenerList         m_pListners               = null;
	private   WGridTableView            m_pMainView               = null; 
	protected WGridTableView            m_pFocusedView            = null;
	private   WGridTableView            m_pMouseView              = null;
	private   WGridTableView            m_pDragView               = null;
	protected ArrayList<WGridTableView> m_pActiveViews            = null;
	private   ArrayList                 m_pMaximizeHiddenControls = null;
	private   BufferedImage             m_pPaintBuffer            = null;
	private   Graphics2D                m_pPaintGraphics          = null;
		
	/**
	 * Default constructor.
	 *
	 */
	public WGridControl()
	{
		m_pListners = new EventListenerList();
		
		m_pFocusedView = new WGridTableView(this);		
		m_pMainView = m_pFocusedView;
		
		m_pActiveViews = new ArrayList<WGridTableView>();
		m_pActiveViews.add(m_pFocusedView);
		
		m_pMaximizeHiddenControls = new ArrayList();
		
		this.setFocusable(true);
		this.setLayout(null);
		this.addKeyListener(new KeyListener(){
			public void keyTyped(KeyEvent e){
				On_KeyTyped(e);
			}
			
			public void keyPressed(KeyEvent e){	
				On_KeyPressed(e);
			}
			
			public void keyReleased(KeyEvent e){	
				On_KeyReleased(e);
			}
		});
		this.addMouseListener(new MouseListener(){
			public void mousePressed(MouseEvent e){
				On_MousePressed(e);
			}
			
			public void mouseReleased(MouseEvent e){
				On_MouseReleased(e);
			}
			
			public void mouseExited(MouseEvent e){
				On_MouseExited(e);
			}
			
			public void mouseEntered(MouseEvent e){
				On_MouseEntered(e);
			}
			
			public void mouseClicked(MouseEvent e){
				On_MouseClicked(e);
			}
		});
		this.addMouseMotionListener(new MouseMotionListener(){
			public void mouseMoved(MouseEvent e)			{
				On_MouseMoved(e);
			}	
			
			public void mouseDragged(MouseEvent e){	
				On_MouseDragged(e);
			}
		});
		this.addMouseWheelListener(new MouseWheelListener(){
			public void mouseWheelMoved(MouseWheelEvent e){
				On_MouseWheelMoved(e);
			}
		});
	}
	
	/**
	 * Clean up any resources being used.
	 */
	public void Dispose()
	{	
		super.Dispose();
		
		if(m_pPaintGraphics != null){
			m_pPaintGraphics.dispose();
			m_pPaintGraphics = null;
		}
		m_pPaintBuffer = null;
	}
	
	
	/**
	 * 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();		
	}
	
	/**
	 * Default paint override.
	 */
	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;
			}
			
			Graphics2D g2 = GetPaintGraphics();
			
			// Fill default grid backround
			g2.setColor(ViewStyle.GetColorFromRgb(212,208,200));
			g2.fillRect(0,0,getBounds().width,getBounds().height);
			
			if(m_pMainView != null){
				m_pMainView.setBounds(new Rectangle(0,0,this.getBounds().width,this.getBounds().height));
				m_pMainView.Paint(g2);
			}
		}
		
		// Draw offscreen image
		g.drawImage(m_pPaintBuffer,0,0,this);
	}
	
	
	/**
	 * Forces grid to repaint all views.
	 *
	 */
	public void Repaint()
	{
		if(m_pPaintGraphics != null){
			m_pPaintGraphics.dispose();
			m_pPaintGraphics = null;
		}
		m_pPaintBuffer = null;
		
		repaint();
	}
	
	
	/**
	 * 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;
	}
	
	
	//--- Forward key events to focused view, if they aren't handled by user ----//
	protected void On_KeyTyped(KeyEvent e)
	{
		if(m_pFocusedView != null){
			m_pFocusedView.Process_keyTyped(e);
		}
	}
	
	protected void On_KeyPressed(KeyEvent e)
	{	
		if(m_pFocusedView != null){
			m_pFocusedView.Process_keyPressed(e);
		}
	}
	
	protected void On_KeyReleased(KeyEvent e)
	{	
		if(m_pFocusedView != null){
			m_pFocusedView.Process_keyReleased(e);
		}
	}
	//----------------------------------------------------------------------------//
	
	//--- Forward mouse events to view under mouse cursor ------------------------//
	protected void On_MousePressed(MouseEvent e)
	{
		WGridTableView view = this.GetMouseInTableView(e.getPoint());
		if(view != null){
			view.Process_mousePressed(e);
		}
	}

	protected void On_MouseReleased(MouseEvent e)
	{
		if(m_pDragView != null){
			m_pDragView.Process_mouseReleased(e);
			m_pDragView = null;
		}
		else{
			WGridTableView view = this.GetMouseInTableView(e.getPoint());
			if(view != null){
				view.Process_mouseReleased(e);
			}
		}
	}
	
	protected void On_MouseClicked(MouseEvent e) 
	{
		WGridTableView view = this.GetMouseInTableView(e.getPoint());
		if(view != null){
			view.Process_mouseClicked(e);
		}
	}

	protected void On_MouseEntered(MouseEvent e)
	{
		WGridTableView view = this.GetMouseInTableView(e.getPoint());
		if(view != null){
			view.Process_mouseEntered(e);
		}
	}

	protected void On_MouseExited(MouseEvent e)
	{
		for(int i=0;i<m_pActiveViews.size();i++){
			WGridTableView view = (WGridTableView)m_pActiveViews.get(i);
			view.Process_mouseExited(e);
		}
		
		// Ensure that default mouse cursor is set. 
		// 
		// There is some trouble with Cursors in java.
		// If control is on JDialog, cursor setting won't work.
		// For workaroud just set JDialog cursor instead.
		if(javax.swing.SwingUtilities.getRoot(this) instanceof JDialog){
			javax.swing.SwingUtilities.getRoot(this).setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		}
	}
	
	protected void On_MouseWheelMoved(MouseWheelEvent e) 
	{
		// Mouse wheel events must be forwarded to keyboard focused view !!!
		if(m_pFocusedView != null){
			m_pFocusedView.Process_mouseWheelMoved(e);
		}
	}
	
	protected void On_MouseMoved(MouseEvent e)
	{
		WGridTableView view = this.GetMouseInTableView(e.getPoint());
		if(view != null){
			view.Process_mouseMoved(e);
		}
	}	
	
	protected void On_MouseDragged(MouseEvent e)
	{	
		if(m_pDragView == null){
			m_pDragView = this.GetMouseInTableView(e.getPoint());
		}
		else{
			m_pDragView.Process_mouseDragged(e);
		}
	}
	//--------------------------------------------------------------------------------//
	
	
	/**
	 * Gets view what is at specified point.
	 */
	private WGridTableView GetMouseInTableView(Point pt)
	{
		// Focused view is in edit mode, skip all mouse events
	//	if(m_pFocusedView != null && m_pFocusedView.getActiveEditor() != null){
	//		return null;
	//	}
		
		for(int i=m_pActiveViews.size() - 1;i>-1;i--){
			WGridTableView view = (WGridTableView)m_pActiveViews.get(i);
			
			if(view.getBounds().contains(pt)){
				if(m_pMouseView != view){
					// Mouse in view changed, send Exit note.
					if(m_pMouseView != null){
						m_pMouseView.Process_mouseExited(null);
					}
					m_pMouseView = view;
				}				
				return view;
			}
		}
		
		return m_pFocusedView;
	}
	
	
	/**
	 * Is called when specified view maximized.
	 * @param view View what maximized,
	 */
	protected void OnViewMaximized(WGridTableView view)
	{
		// Hide all grid components which owner isn't maximized view		
		Component[] c = this.getComponents();
		ArrayList viewC = view.getComponents();
		for(int i=0;i<c.length;i++){
			Component comp = (Component)c[i];
			if(comp.isVisible()){
				if(!viewC.contains(comp)){
					comp.setVisible(false);
					m_pMaximizeHiddenControls.add(comp);
				}
			}
		}
	}
	
	/**
	 * Is called when specified view minimized.
	 * @param view View what minimized.
	 */
	protected void OnViewMinimized(WGridTableView view)
	{
		// Show all grid components which was hidden for maximized view
		for(int i=0;i<m_pMaximizeHiddenControls.size();i++){
			Component c = (Component)m_pMaximizeHiddenControls.get(i);
			c.setVisible(true);
		}
	}
	
	
	/**
	 * Gets grid main view.
	 * @return
	 */
	public WGridTableView getMainView()
	{
		return m_pMainView;
	}
	/**
	 * Sets grid main view.
	 * @param value
	 */
	public void setMainView(WGridTableView value)
	{
		if(m_pMainView != null){
			for(Object c : m_pMainView.getComponents()){
				remove((Component)c);
			}
			m_pActiveViews.remove(m_pMainView);
		}
		
		m_pMainView = value;
		m_pFocusedView = value;
		m_pActiveViews.add(value);
		Repaint();
	}
	
	/**
	 * Gets keyboard focused view.
	 * @return
	 */
	public WGridTableView getFocusedView()
	{
		return m_pFocusedView;
	}
	
	/**
	 * Gets active views (Main view + all open child views).
	 *
	 */
	public WGridTableView[] getActiveViews()
	{
		WGridTableView[] retVal = new WGridTableView[m_pActiveViews.size()];
		m_pActiveViews.toArray(retVal);
		
		return retVal;
		
		// Won't work  for some reason
	//	return (WGridTableView[])m_pActiveViews.toArray();
	}
	
					
	/**
	 * Adds the specified action listener to receive action events.
	 * @param l the action listener
	 */ 
	public void addGridListener(WGridListener l) 
	{
		m_pListners.add(WGridListener.class,l);
	}

	/**
	 * Removes the specified action listener so it no longer receives action events.
	 * @param l the action listener
	 */ 
	public void removeGridListener(WGridListener l) 
	{
		m_pListners.remove(WGridListener.class,l);
	}
	
	/**
	 * Raises row double clicked event.
	 * @param dr
	 */
	protected void RaiseOnRowDoubleClicked(DataRowView dr)
	{
		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] == WGridListener.class){
				// pass the event to the listeners event dispatch method
				((WGridListener)listeners[i+1]).OnRowDoubleClicked(dr);
			}            
		}
	}
	
	/**
	 * Raises active row changed event.
	 * @param dr
	 */
	protected void RaiseOnActiveRowChanged(DataRowView dr)
	{
		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] == WGridListener.class){
				// pass the event to the listeners event dispatch method
				((WGridListener)listeners[i+1]).OnActiveRowChanged(dr);
			}            
		}
	}
	
	/**
	 * Raises add row event.
	 * @param dr
	 * @return
	 */
	protected boolean RaiseAddNewRow(DataRowView dr)
	{
		boolean retVal = true;
		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] == WGridListener.class){
				// pass the event to the listeners event dispatch method
				if(!((WGridListener)listeners[i+1]).OnAddRow(dr)){
					retVal = false;
				}
			}            
		}
		
		return retVal;
	}
	
	/**
	 * Raises delete row event.
	 * @param dr
	 * @return Returns true if row update was successfull.
	 */
	protected boolean RaiseDeleteRow(DataRowView dr)
	{
		boolean retVal = true;
		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] == WGridListener.class){
				// pass the event to the listeners event dispatch method
				if(!((WGridListener)listeners[i+1]).OnDeleteRow(dr)){
					retVal = false;
				}
			}            
		}
		
		return retVal;
	}
	
	/**
	 * Raises update row event.
	 * @param dr Row what is being updated.
	 * @return Returns true if row update was successfull.
	 */
	protected boolean RaiseUpdateRow(DataRowView dr)
	{
		boolean retVal = true;
		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] == WGridListener.class){
				// pass the event to the listeners event dispatch method
				if(!((WGridListener)listeners[i+1]).OnUpdateRow(dr)){
					retVal = false;
				}
			}            
		}
		
		return retVal;
	}
	
	/**
	 * Raises validates cell value event.
	 * @param column
	 * @param dr
	 * @param value
	 * @return Returns true if cell value is valid.
	 */
	protected boolean RaiseValidateCellValue(WGridColumn column,DataRowView dr,Object value)
	{
		boolean retVal = true;
		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] == WGridListener.class){
				// pass the event to the listeners event dispatch method
				if(!((WGridListener)listeners[i+1]).OnValidateCellValue(column,dr,value)){
					retVal = false;
				}
			}            
		}
		
		return retVal;
	}
	
	/**
	 * Raises NewItemRowAdded event.
	 * @param dr New row what added.
	 */
	protected void RaiseNewItemRowAdded(DataRowView dr)
	{
		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] == WGridListener.class){
				// pass the event to the listeners event dispatch method
				((WGridListener)listeners[i+1]).OnNewItemRowAdded(dr);
			}            
		}
	}
	
	/**
	 * Raises OnMaxMinButton pressed event.
	 * @param maximized
	 */
	protected void RaiseOnMaxMinButtonPressed(boolean maximized)
	{	
		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] == WGridListener.class){
				// pass the event to the listeners event dispatch method
				((WGridListener)listeners[i + 1]).OnMaxMinButtonPressed(maximized);
			}            
		}
	}

}
