/*
 * Created on 27.01.2006
 *
 */
package org.jdesktop.swingx.action;

import java.awt.event.ActionEvent;
import java.awt.event.ItemListener;
import java.util.logging.Logger;

import javax.swing.AbstractButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JRadioButton;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JToggleButton;

import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.junit.Test;
import org.junit.Before;
import org.junit.After;


import junit.framework.TestCase;

@RunWith(JUnit4.class)
public class ActionTest extends TestCase {
    @SuppressWarnings("all")
    private static final Logger LOG = Logger.getLogger(ActionTest.class
            .getName());
    protected ActionContainerFactory factory;
    
    @Before
    public void setUpJ4() throws Exception {
        setUp();
    }
    
    @After
    public void tearDownJ4() throws Exception {
        tearDown();
    }
    
   
    
    @Override
    protected void setUp() throws Exception {
        factory = new ActionContainerFactory(null);
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * 
     * test that configured button is kept in synch with
     *  action selected state and the other way round. This uses a 
     *  custom configure via the ActionContainerFactory.configureSelectableButton.
     * The direction from button to action is broken.
     */
    @Test
    public void testButtonSelectedSynchAction() {
        AbstractActionExt extAction = createStateAction();
        boolean actionSelected = true;
        extAction.setSelected(actionSelected);
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // invert action selected and assert that the change is taken up
        // by the button
        extAction.setSelected(!actionSelected);
        assertEquals("button selected must be synched to action", 
                !actionSelected, button.isSelected());
        // reset button 
        button.setSelected(actionSelected);
        // sanity: the button did take the direct selection change
        assertEquals(actionSelected, button.isSelected());
        // assert that changed selected is taken up by action
        assertEquals("action selected must be synched to button", 
                actionSelected, extAction.isSelected());
    }


    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * 
     * test that configured button is kept in synch with
     *  action selected state and the other way round. This uses the 
     *  ActionContainerFactory.createButton().
     * The direction from button to action is broken.
     */
    @Test
    public void testCreateButtonSelectedSynchAction() {
        AbstractActionExt extAction = createStateAction();
        boolean actionSelected = true;
        extAction.setSelected(actionSelected);
        JToggleButton button = (JToggleButton) factory.createButton(extAction);
        // invert action selected and assert that the change is taken up
        // by the button
        extAction.setSelected(!actionSelected);
        assertEquals("button selected must be synched to action", 
                !actionSelected, button.isSelected());
        // reset button 
        button.setSelected(actionSelected);
        // sanity: the button did take the direct selection change
        assertEquals(actionSelected, button.isSelected());
        // assert that changed selected is taken up by action
        assertEquals("action selected must be synched to button", 
                actionSelected, extAction.isSelected());
    }



    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     *  
     * test that the button always has mostly one
     * Action registered as itemListener and that
     * this registered listener is the same as the buttons
     * action.
     *
     */
    @Test
    public void testButtonOneActionAsItemListener() {
        AbstractActionExt extAction = createStateAction();
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        assertCountAsItemListener(button, extAction, 1);
        factory.configureSelectableButton(button, null, null);
        // assert that the previous action is removed as itemListener
        assertCountAsItemListener(button, extAction, 0);
    }
    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that configured button is kept in synch with
     *  maximal one action's selected state
     */
    @Test
    public void testButtonSelectedNullAction() {
        AbstractActionExt extAction = createStateAction();
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // we are sure that the button selected is true (has dedicated test)
        // now configure it with a different action, unselected
        AbstractActionExt extActionB = createStateAction();
        factory.configureSelectableButton(button, extActionB, null);
        // invert the old action selected and assert that the change 
        // does not effect the taken up by the button
        extAction.setSelected(!extAction.isSelected());
        assertEquals("button selected must be uneffected by old action",
                extActionB.isSelected(), button.isSelected());
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that PCLs related to a previous button are 
     * unregistered from the Action after release.
     *
     */
    @Test
    public void testButtonReleaseActionReleasePCL() {
        AbstractActionExt extAction = createStateAction();
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // sanity: expect it to be 2 - one is the menuitem itself, another 
        // the TogglePCL registered by the ActionContainerFacory
        assertEquals(2, extAction.getPropertyChangeListeners().length);
        // set the button's action to null
        factory.configureSelectableButton(button, null, null);
        // assert that button related PCLs are removed from the action's listener list
        assertEquals(0, extAction.getPropertyChangeListeners().length);
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that configured button is no longer kept in
     * synch after setting the action to null.
     */
    @Test
    public void testButtonSelectedReleasedSynchAction() {
        AbstractActionExt extAction = createStateAction();
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // now we unconfigure it with a null action
        factory.configureSelectableButton(button, null, null);
        // invert the old action selected and assert that the change 
        // does not effect the taken up by the button
        boolean oldSelected = button.isSelected();
        extAction.setSelected(!extAction.isSelected());
        assertEquals("button selected must be uneffected by old action",
                oldSelected, button.isSelected());
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that configured button is kept in synch with
     *  maximal one action's selected state
     */
    @Test
    public void testButtonSelectedMaxOneSynchAction() {
        AbstractActionExt extAction = createStateAction();
        boolean actionSelected = true;
        extAction.setSelected(actionSelected);
        JToggleButton button = new JToggleButton();
        factory.configureSelectableButton(button, extAction, null);
        // we are sure that the button selected is true (has dedicated test)
        // now configure it with a different action, unselected
        AbstractActionExt extActionB = createStateAction();
        factory.configureSelectableButton(button, extActionB, null);
        // sanity: the new action is not effected by the old
        // currently this may accidentally pass because the back direction isn't
        // synched!! 
        assertFalse(extActionB.isSelected());
        assertEquals("button selected must be initialized to new action",
                extActionB.isSelected(), button.isSelected());
        // invert the old action selected and assert that the change 
        // does not effect the taken up by the button
        extAction.setSelected(!actionSelected);
        // need to be done twice, the first toggle produces 
        extAction.setSelected(actionSelected);
        assertEquals("button selected must be uneffected by old action",
                extActionB.isSelected(), button.isSelected());
    }

    /**
     * Issue #255-swingx: probs in synch selectable button <--> action. 
     * test that button is configured with initial action selected state.
     *
     */
    @Test
    public void testButtonSelectedInitialSynchAction() {
        AbstractActionExt extAction = createStateAction();
        boolean actionSelected = true;
        extAction.setSelected(actionSelected);
        JToggleButton button = new JToggleButton();
        boolean buttonSelected = button.isSelected();
        // sanity: different selected state
        assertTrue(actionSelected != buttonSelected);
        factory.configureSelectableButton(button, extAction, null);
        assertEquals("action selection must be unchanged", actionSelected, extAction.isSelected());
        assertEquals("button selected must be initialized", actionSelected, button.isSelected());
    }

    /**
     * test method contract: configureSelectable must throw runtime exception
     * for non-state action.
     *
     */
    @Test
    public void testExceptionOnNonStateAction() {
        AbstractActionExt actionExt = createStateAction();
        actionExt.setStateAction(false);
        JToggleButton button = new JToggleButton();
        try {
           factory.configureSelectableButton(button, actionExt, null);
           fail("configureSelectable didn't throw IllegalArgument for non-state action ");
        } catch (IllegalArgumentException e) {
            // nothing todo - this is what we expect
        } catch (Exception e) {
            fail("caught unexpected exception " + e);
        }
        
    }

    /**
     * Issue #229-swingx: increasing listener list in column actions.
     * 
     * sub-issue: ActionContainerFactory doesn't check if the action is
     * already synchronizing to the same button. 
     */
    @Test
    public void testToggleButtonConfigure() {
        // this should pass after giving the gc "reasonable" chance to
        // have collected the unreachable...
//        assertToggleButtonConfigure(new JToggleButton(), new JToggleButton());

    }

    /**
     * Issue #229-swingx: increasing listener list in column actions.
     * 
     * sub-issue: ActionContainerFactory doesn't check if the action is
     * already synchronizing to the same button. 
     */
    @Test
    public void testToggleButtonConfigureToggleWithSame() {
        assertToggleButtonConfigureWithSame(new JToggleButton());
        assertToggleButtonConfigureWithSame(new JRadioButton());
        assertToggleButtonConfigureWithSame(new JCheckBox());
        assertToggleButtonConfigureWithSame(new JRadioButtonMenuItem());
        assertToggleButtonConfigureWithSame(new JCheckBoxMenuItem());

    }
    
    private void assertToggleButtonConfigureWithSame(AbstractButton button) {
        AbstractActionExt extAction = createStateAction();
        assertEquals(0, extAction.getPropertyChangeListeners().length);
        factory.configureSelectableButton(button, extAction, null);
        // sanity: expect it to be 2 - one is the menuitem itself, another 
        // the TogglePCL registered by the ActionContainerFacory
        
        assertEquals(2, extAction.getPropertyChangeListeners().length);
        factory.configureSelectableButton(button, extAction, null);
        // JW: wrong assumption!! Valid only if first == second, for
        // different buttons we actually expect the listener count to be increased!
        // Note to myself: remove this comment after correcting the method call
        // sequence here in the test ...
        assertEquals(2, extAction.getPropertyChangeListeners().length);
        
    }

    /**
     * Issue #229-swingx: increasing listener list in column actions.
     * 
     * sub-issue: ActionContainerFactory registers the action multiple times to
     * the same button as ItemListener
     */
    @Test
    public void testToggleButtonAddItemListenerToSame() {
        assertAddItemListenerToSame(new JToggleButton());
        assertAddItemListenerToSame(new JRadioButton());
        assertAddItemListenerToSame(new JCheckBox());
        assertAddItemListenerToSame(new JRadioButtonMenuItem());
        assertAddItemListenerToSame(new JCheckBoxMenuItem());
    }
    
    private void assertAddItemListenerToSame(AbstractButton checkBoxItem) {
        AbstractActionExt extAction = createStateAction();
        factory.configureSelectableButton(checkBoxItem, extAction, null);
        assertCountAsItemListener(checkBoxItem, extAction, 1 );
        factory.configureSelectableButton(checkBoxItem, extAction, null);
        assertCountAsItemListener(checkBoxItem, extAction, 1 );
      
    }

    protected AbstractActionExt createStateAction() {
        AbstractActionExt extAction = new AbstractActionExt("dummy") {

            public void actionPerformed(ActionEvent e) {
            }
            
        };
        extAction.setStateAction();
        return extAction;
    }

    /**
     * assert that the given itemListener is registered exactly
     * expectedCount times to the given button.
     * @param checkBoxItem
     * @param extAction
     * @param expectedCount
     */
    protected void assertCountAsItemListener(AbstractButton checkBoxItem, ItemListener extAction, int expectedCount) {
        int count = 0;
        ItemListener[] itemListeners = checkBoxItem.getItemListeners();
        for (int j = 0; j < itemListeners.length; j++) {
            if (extAction == itemListeners[j]) {
                count++;
            }
        }
        assertEquals("ItemListener registration count", expectedCount, count);
        
    }

    /**
     * Issue #4-swinglabs: infinite loop when setting long destricption.
     *
     */
    @Test
    public void testLongDescriptionLoop() {
        AbstractActionExt action = createStateAction();
        action.setLongDescription("some");
    }
}
