package net.sourceforge.squirrel_sql.client.session;
/*
 * Copyright (C) 2001-2004 Colin Bell
 * [email protected]
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;

import javax.swing.Action;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import net.sourceforge.squirrel_sql.fw.gui.TextPopupMenu;
import net.sourceforge.squirrel_sql.fw.gui.action.BaseAction;
import net.sourceforge.squirrel_sql.fw.util.DefaultExceptionFormatter;
import net.sourceforge.squirrel_sql.fw.util.ExceptionFormatter;
import net.sourceforge.squirrel_sql.fw.util.IMessageHandler;
import net.sourceforge.squirrel_sql.fw.util.StringManager;
import net.sourceforge.squirrel_sql.fw.util.StringManagerFactory;
import net.sourceforge.squirrel_sql.fw.util.log.ILogger;
import net.sourceforge.squirrel_sql.fw.util.log.LoggerController;
/**
 * This is the message panel at the bottom of the session sheet.
 *
 * @author <A HREF="mailto:[email protected]">Colin Bell</A>
 */
public class MessagePanel extends JTextPane implements IMessageHandler
{
	/** Logger for this class. */
	private static final ILogger s_log =
		LoggerController.createLogger(MessagePanel.class);

    /** Internationalized strings for this class. */
    private static final StringManager s_stringMgr =
        StringManagerFactory.getStringManager(MessagePanel.class);

   /** Popup menu for this component. */
	private final TextPopupMenu _popupMenu = new MessagePanelPopupMenu();

	/**
	 * Attribute sets for error and last message.
	 */
	private SimpleAttributeSet _saSetMessage;
//	private SimpleAttributeSet _saSetErrorHistory;
	private SimpleAttributeSet _saSetError;
	private SimpleAttributeSet _saSetWarning;

	/**
	 * Save into these attributes the parameters of the last message being output.
	 * @todo In the near future: if more than one message shall be remembered, then these variables
	 * need to be replaced with a dynamic storage (ArrayList or similar).
	 */
	private int _lastLength;
	private String _lastMessage;
	private SimpleAttributeSet _lastSASet;

   private HashMap<SimpleAttributeSet, SimpleAttributeSet> _saSetHistoryBySaSet 
       = new HashMap<SimpleAttributeSet, SimpleAttributeSet>();

   private static interface I18N {
       //i18n[MessagePanel.clearLabel=Clear]
       String CLEAR_LABEL = s_stringMgr.getString("MessagePanel.clearLabel");
   }
   
   private DefaultExceptionFormatter defaultExceptionFormatter = 
       new DefaultExceptionFormatter();
   
   /**
    * Default ctor.
    */
   public MessagePanel()
   {
      super();

      _popupMenu.setTextComponent(this);

      // Add mouse listener for displaying popup menu.
      addMouseListener(new MouseAdapter()
      {
         public void mousePressed(MouseEvent evt)
         {
            if (evt.isPopupTrigger())
            {
               _popupMenu.show(evt);
            }
         }
         public void mouseReleased(MouseEvent evt)
         {
            if (evt.isPopupTrigger())
            {
               _popupMenu.show(evt);
            }
         }
      });

      ///////////////////////////////////////////////////////////////////
      // Message
      _saSetMessage = new SimpleAttributeSet();
      StyleConstants.setBackground(_saSetMessage, Color.green);

      SimpleAttributeSet saSetMessageHistory = new SimpleAttributeSet();
      StyleConstants.setBackground(saSetMessageHistory, getBackground());
      _saSetHistoryBySaSet.put(_saSetMessage, saSetMessageHistory);
      //
      ////////////////////////////////////////////////////////////////


      ///////////////////////////////////////////////////////////////////
      // Warning
      _saSetWarning = new SimpleAttributeSet();
      StyleConstants.setBackground(_saSetWarning, Color.yellow);

      SimpleAttributeSet saSetWarningHistory = new SimpleAttributeSet();
      StyleConstants.setBackground(saSetWarningHistory, new Color(255,255,210)); // a light yellow
      _saSetHistoryBySaSet.put(_saSetWarning, saSetWarningHistory);
      //
      ////////////////////////////////////////////////////////////////


      /////////////////////////////////////////////////////////////////
      // Error
      // Attention: Do not use background colors here.
      // Color blind people cannot read black writing on red background.
      _saSetError = new SimpleAttributeSet();
      //StyleConstants.setBackground(_saSetError, Color.red);
      StyleConstants.setForeground(_saSetError, Color.red);

      SimpleAttributeSet saSetErrorHistory = new SimpleAttributeSet();
      //StyleConstants.setBackground(saSetErrorHistory, Color.pink);
      StyleConstants.setForeground(saSetErrorHistory, new Color(255,102,102));
      _saSetHistoryBySaSet.put(_saSetError, saSetErrorHistory);
      //
      //////////////////////////////////////////////////////////////////


   }


   public void addToMessagePanelPopup(Action action)
   {
       if (action == null) {
           throw new IllegalArgumentException("action cannot be null");
       }
      _popupMenu.add(action);   
   }


   /**
    * Show a message describing the passed throwable object.
    *
    * @param th	The throwable object.
    * @param session the session that generated the exception.
    */
   public synchronized void showMessage(final Throwable th, 
                                        final ExceptionFormatter formatter)
   {
      if (th == null)
      {
          throw new IllegalArgumentException("th cannot be null");
      }
      privateShowMessage(th, formatter, _saSetMessage);
   }
   

   /**
	 * Show an error message describing the passed exception. The controls
	 * background color will be changed to show it is an error msg.
	 *
	 * @param	th		Exception.
     * @param session the session that generated the exception. 
	 */
	public synchronized void showErrorMessage(final Throwable th, 
                                              ExceptionFormatter formatter)
	{
		if (th == null)
		{
            throw new IllegalArgumentException("th cannot be null");
		}
        privateShowMessage(th, formatter, _saSetError);
	}


   /**
    * Show a message.
    *
    * @param msg	The message to be shown.
    */
   public synchronized void showMessage(final String msg)
   {
      if (msg == null)
      {
          throw new IllegalArgumentException("msg cannot be null");
      }
      privateShowMessage(msg, _saSetMessage);
   }

   public void showWarningMessage(String msg)
   {
      if (msg == null)
      {
          throw new IllegalArgumentException("msg cannot be null");
         
      }
      privateShowMessage(msg, _saSetWarning);
   }
   
   /**
	 * Show an error message. The controls
	 * background color will be changed to show it is an error msg.
 * @param	th		Exception.
	 */
	public synchronized void showErrorMessage(final String msg)
	{
		if (msg == null)
		{
			throw new IllegalArgumentException("msg cannot be null");
		}
        privateShowMessage(msg, _saSetError);
	}


   /**
    * Private method, the real implementation of the corresponding show*Message methods.
    *
    * @param th	The throwable whose details shall be displayed.
    * @param saSet The SimpleAttributeSet to be used for message output.
    */
   private void privateShowMessage(final Throwable th, 
                                   final ExceptionFormatter formatter, 
                                   final SimpleAttributeSet saSet)
   {
      if (th != null) {
          
          String message = "";
          if (formatter == null) {
              message = defaultExceptionFormatter.format(th);
          } else {
              try {
                  message = formatter.format(th);
              } catch (Exception e) {
                  s_log.error("Unable to format message: "+e.getMessage(), e);
              }
          }
          privateShowMessage(message, saSet);
          s_log.error("privateShowMessage: Exception was "+th.getMessage(), th);
      }
   }
   
	/**
	 * Private method, the real implementation of the corresponding show*Message methods.
	 *
	 * @param msg	The message to be displayed.
	 * @param saSet	The SimpleAttributeSet to be used for message output.
	 */
	private void privateShowMessage(final String msg, final SimpleAttributeSet saSet)
	{
		if (msg == null)
		{
			throw new IllegalArgumentException("null Message");
		}

		// Thread safe support for every call to this method:
		SwingUtilities.invokeLater(new Runnable()
		{
			public void run()
			{
				addLine(msg, saSet);
			}
		});
	}

	/**
	 * Do the real appending of text to the message panel. The last message is always highlighted.
	 * @todo Highlight not only the last message, but all messages from SQL statements which were run together.
	 *
	 * @param string	The String to be appended.
	 * @param saSet		The SimpleAttributeSet to be used for for the string.
	 */
	private void append(String string, SimpleAttributeSet saSet)
	{
		Document document = getStyledDocument();
		try
		{
         /////////////////////////////////////////////////////////////////////////////////
         // Checks if the former message should be highlighted in a 'history' color.
         if (document.getLength() >= _lastLength && null != _lastMessage)
			{
            SimpleAttributeSet historySaSet = _saSetHistoryBySaSet.get(_lastSASet);
            document.remove(_lastLength, _lastMessage.length());
            document.insertString(document.getLength(), _lastMessage, historySaSet);
			}
         //
         ///////////////////////////////////////////////////////////////////////////////////

         _lastLength = document.getLength();
			_lastMessage = string;
			_lastSASet = saSet;

			document.insertString(document.getLength(), string, saSet);
		}
		catch (BadLocationException ble)
		{
			s_log.error("Error appending text to MessagePanel document.", ble);
		}
	}

	/**
	 * Add the passed line to the end of the messages display. Position
	 * display so the the newly added line will be displayed.
	 *
	 * @param line		The line to be added.
	 * @param saSet		The SimpleAttributeSet to be used for for the string.
	 */
	private void addLine(String line, SimpleAttributeSet saSet)
	{
		if (getDocument().getLength() > 0)
		{
			append("\n", saSet);
		}
		append(line, saSet);
		final int len = getDocument().getLength();
		select(len, len);
	}

	/**
	 * Popup menu for this message panel.
	 */
	private class MessagePanelPopupMenu extends TextPopupMenu
	{
        static final long serialVersionUID = -425002646648750251L;
        
		public MessagePanelPopupMenu()
		{
			super();
			add(new ClearAction());
		}

		/**
		 * Class handles clearing the message area. Resets the background
		 * colour (to get rid of error msg colour) as well as clearing
		 * the text.
		 */
		private class ClearAction extends BaseAction
		{
            static final long serialVersionUID = 2124058843445088350L;
			protected ClearAction()
			{
				super(I18N.CLEAR_LABEL);
			}

			public void actionPerformed(ActionEvent evt)
			{
				try
				{
				    Document doc = MessagePanel.this.getDocument();
				    doc.remove(0, doc.getLength());
				    _lastMessage = null;
				}
				catch (BadLocationException ex)
				{
					s_log.error("Error clearing document", ex);
				}
			}
		}
	}
}