package textbender.a.r.page; // Copyright 2006-2007, Michael Allan.  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE.

import java.awt.*;
import java.awt.event.*;
import java.rmi.*;
import javax.swing.*;
import javax.swing.event.*;
import org.w3c.dom.*;
import org.w3c.dom.events.*;
import textbender.a.r.page.navdo.*;
import textbender.a.u.branch.*;
import textbender.d.gene.*;
import textbender.g.lang.*;
import textbender.g.util.logging.*;
import textbender.o.awt.*;
import textbender.o.rhinohide.events.RelaySIP;
import textbender.o.swing.*;


/** A pop-up menu for control of in-page tools.
  * <p>
  *     Pop it context-aware by mouse (Ctrl-anyButton).
  *     </p>
  * <p>
  *     Pop it context-free by mouse (anyButton) in the toolbar applet.
  *     For {@linkplain PageVisit#isPageSilent() silent pages},
  *     this is the only way to pop the menu.
  *     </p>
  * <p>
  *     Pop it context-free by keystroke (Ctrl-T).
  *     [Maybe context-aware later, if we track browser's on-screen cursor.]
  *     Child menu-items will usually have mnemonics,
  *     making them invokable by additional keystrokes.
  *     </p>
  * <p>
  *     Mouse and key bindings are hard-coded. [Will later be configurable,
  *      per a/z : general end-user config framework.]
  *     </p>
  */
final class PopupMenu
{


    /** Constructs a PopupMenu, and installs it in-page.
      * It automatically releases itself at page exit.
      * There is no way to release it before then.
      * <p>
      *     Also gives the applet a hand cursor, as a cue.
      *     </p>
      *
      *     @param vP page visit, context
      */
    @ThreadRestricted("AWT event dispatch") PopupMenu( PageVisit vP ) throws RemoteException
    {
        assert java.awt.EventQueue.isDispatchThread();
        pageVisit = vP;

      // Dismisser button. Workaround for bug 'unwanted-guest'.
      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      //popupMenuV.add( Box.createVerticalStrut( 10 ));
      ///// not clickable, so try:
        {
            JMenuItem button = new JMenuItem( "" );
            button.setEnabled( false );
            popupMenuV.add( button );

            button.setBackground( ColorX.offBright( button.getBackground(), 0.05f ));
        }

      // Basic buttons.
      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        {
            JMenuItem button = new JMenuItem( pageVisit.edt().reloadAction() ); // no need to unregister, registry does not outlive this listener
            button.setMnemonic( KeyEvent.VK_L );
            popupMenuV.add( button );
        }
        {
            JMenuItem button = new JMenuItem( pageVisit.edt().saveAction() );
            button.setMnemonic( KeyEvent.VK_S );
            popupMenuV.add( button );
        }

        popupMenuV.addSeparator();
        {
            JMenuItem button = new JMenuItem( new TestAction( pageVisit ));
            popupMenuV.add( button );
        }
        {
            JMenuItem button = new JMenuItem( new InPageBrancher( pageVisit ));
            popupMenuV.add( button );
        }
     // {
     //     JCheckBoxMenuItem button = new JCheckBoxMenuItem( "Locus Pointer" );
     //     button.setModel( new EnableButton( pageVisit, pageVisit.spool() ));
     //     button.setIcon( ImageIconX.tryCreate( getClass().getClassLoader().getResource
     //         ( "toolbarButtonGraphics/development/BeanAdd16.gif" ), 16 )); // from textbender/o/jlfgr.jar
     //     popupMenuV.add( button );
     // }

      // Temporary test buttons.
      // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     // popupMenuV.addSeparator();
     // {
     //     JMenuItem button = new JMenuItem( "Test 1" ); // PRIVATE
     //     button.addActionListener( new java.awt.event.ActionListener()
     //     {
     //         public void actionPerformed( java.awt.event.ActionEvent e )
     //         {
     //             System.out.println( PageDaemons.i().connections().transferCHub().getTransferand() );
     //         }
     //     });
     //     popupMenuV.add( button );
     // }
     // {
     //     JMenuItem button = new JMenuItem( "Test 2" ); // PRIVATE
     //     button.addActionListener( new java.awt.event.ActionListener()
     //     {
     //         public void actionPerformed( java.awt.event.ActionEvent e )
     //         {
     //          // registry.removeEventListener( "mouseover", testListener, /*use capture*/false );
     //         }
     //     });
     //     popupMenuV.add( button );
     // }

      // - - -
        new Popper();
    }



//// P r i v a t e ///////////////////////////////////////////////////////////////////////


    /** PopupMenu view.
      */
    private final JPopupMenu popupMenuV = new JPopupMenu();



    private final PageVisit pageVisit;



   // ====================================================================================


    private final class Popper extends RelaySIP

        implements java.awt.event.MouseListener, PopupMenuListener
    {

        Popper()
        {
            assert java.awt.EventQueue.isDispatchThread();
            popupMenuV.addPopupMenuListener( Popper.this ); // no need to unregister, registry does not outlive this listener

          // Pop from page's applet.
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            pageVisit.applet().toolbar.addMouseListener( Popper.this ); // no need to unregister, registry does not outlive this listener
         // pageVisit.applet().toolbar.setCursor( Cursor.getPredefinedCursor( Cursor.HAND_CURSOR ));

          // Pop from page.
          //
          // If you ever need to disable the context menu in Gecko browsers
          // (maybe to prevent it appearing simultaneously with this menu)
          // look into altering JavaScript preferences:
          // window.oncontextmenu = function () { return false; }
          // <http://www.thescripts.com/forum/thread163277.html
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            if( !pageVisit.isPageSilent() )
            {
                if( !pageVisit.document().getImplementation().hasFeature( "Events", "2.0" )) throw new UnsupportedOperationException( "browser does not support DOM level 2 events" );

             // pageVisit.window().addEventListener( "keydown", Popper.this, /*use capture*/false ); // Futile attempt to solve glitch. Sometimes keystrokes generate no event, apparently because the document or something loses focus (mouse-clicking in the window restores events). Firefox's own key bindings (Ctrl-T for New tab) have the same problem.
                ((EventTarget)pageVisit.document()).addEventListener( "keydown", Popper.this, /*use capture*/false ); // no need to unregister, registry does not outlive this listener
                ((EventTarget)pageVisit.document()).addEventListener( "mousedown", Popper.this, /*use capture*/false );
            }
        }


        private @ThreadSafe void ensureUnpopped() // work around bug 'unwanted-guest'
        {
            EventQueue.invokeLater( new Runnable()
            {
                public void run() // in AWT event dispatch thread
                {
                    if( !popupMenuV.isVisible() ) return;

                    LoggerX.i(getClass()).finest( "dismissing menu, 'unwanted-guest'" );
                    popupMenuV.setVisible( false );
                }
            });
        }


        private @ThreadSafe void popInText( org.w3c.dom.events.Event e,
            final int x, final int y, final Node targetNode )
        {
            e.preventDefault(); // fails for key event in plain Mozilla 1.7.13, and Firefox on Windows; but works in Firefox on Linux
         // EventQueueX.invokeNowOrWait( new Runnable() // now while e properties are current
         //// hangs Firefox
            EventQueue.invokeLater( new Runnable()
            {
                public void run() // in AWT event dispatch thread
                {
                    final Object e = null; // mask e, because its state may have changed by this time

                    userNode = targetNode;
                    if( x == -1 )
                    {
                        assert y == -1;
                        JComponent invoker = pageVisit.applet().toolbar;
                        popupMenuV.show( invoker, /*x*/0, /*y*/invoker.getHeight() );
                    }
                    else
                    {
                        popupMenuV.setInvoker( pageVisit.applet().toolbar );
                        popupMenuV.setLocation( x,y );
                        popupMenuV.setVisible( true ); // BUG: unwanted-guest. It might not vanish promptly, when it's supposed to; especially when invoked by this mouse event, in the page text. But the workarounds seem to be working...
                     // popupMenuV.show( /*invoker*/pageVisit.applet().toolbar, 0,0 );
                     //// no help for bug 'unwanted-guest'
                    }
                }
            });
        }


        @ThreadRestricted("AWT event dispatch") Node userNode; // target of current or previous pop


       // - E v e n t - L i s t e n e r --------------------------------------------------


        public @ThreadSafe void handleEvent( org.w3c.dom.events.Event _e )
        {
         // System.out.println( "PM.P event: " + _e );
            if( _e instanceof textbender.o.rhinohide.events.KeyEvent )
            {
                final textbender.o.rhinohide.events.KeyEvent e =
                    (textbender.o.rhinohide.events.KeyEvent)_e;
                final int keyCode = e.getKeyCode();
                if( keyCode == java.awt.event.KeyEvent.VK_ESCAPE ) ensureUnpopped(); // specific, to exclude qualifier keys (shift etc.); and to exclude the stream of events apparently generated on Windows when Ctrl is held (!)
                else if( e.getCtrlKey() && keyCode == java.awt.event.KeyEvent.VK_T )
                {
                 // popInText( e, pageVisit.window().getScreenX() + 10,
                 //               pageVisit.window().getScreenY() + 30, /*targetNode*/null );
                    popInText( e, -1, -1, /*targetNode*/null );
                }
            }
            else // mouse
            {
                final org.w3c.dom.events.MouseEvent e = (org.w3c.dom.events.MouseEvent)_e;
                final int button = e.getButton();
                if( e.getCtrlKey() &&( button == 0 || button == 1 || button == 2 ))
                {
                    Node targetNode = null; // till proven otherwise
                    EventTarget target = e.getTarget();
                    if( target instanceof Node ) targetNode = (Node)target;
                    popInText( e, e.getScreenX(), e.getScreenY(), targetNode );
                }
                else ensureUnpopped();
            }
        }


       // - M o u s e - L i s t e n e r --------------------------------------------------


        public void mouseClicked( java.awt.event.MouseEvent e ) {}


        public void mouseEntered( java.awt.event.MouseEvent e ) {}


        public void mouseExited( java.awt.event.MouseEvent e ) {}


        public void mousePressed( java.awt.event.MouseEvent e ) // any button, whether popupMenuV.isPopupTrigger() or not
        {
            userNode = null;
            popupMenuV.show( e.getComponent(), e.getX(), e.getY() );
        }


        public void mouseReleased( java.awt.event.MouseEvent e ) {}


       // - P o p u p - M e n u - L i s t e n e r ----------------------------------------


        public void popupMenuCanceled( PopupMenuEvent e ) {}


        public void popupMenuWillBecomeInvisible( PopupMenuEvent e )
        {
         // pageVisit.user().setNode( null );
         ///// too fast, disables context dependent buttons before click takes effect
            EventQueue.invokeLater( new Runnable()
            {
                public void run() { pageVisit.user().setNode( null ); } // in next AWT event dispatch
            });
        }


        public void popupMenuWillBecomeVisible( PopupMenuEvent e )
        {
            pageVisit.user().setNode( userNode );
        }


    };



}