/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * AbstractToolbarFactory.java
 *
 * Created on January 25, 2004, 11:59 AM
 */

package org.netbeans.actions.engine.spi;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.beans.BeanInfo;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import org.netbeans.actions.engine.spi.ToolbarFactory;
import org.netbeans.actions.spi.ActionProvider;

/**
 *
 * @author  Tim Boudreau
 */
public class AbstractToolbarFactory extends ToolbarFactory {
    private AbstractEngine engine;
    protected static final String KEY_CONTAINERCTX = "containerContext";
    protected static final String KEY_ACTION = "action";  //NOI18N
    protected static final String KEY_CREATOR = "creator"; //NOI18N
    /** Creates a new instance of AbstractToolbarFactory */
    public AbstractToolbarFactory(AbstractEngine engine) {
        if (engine == null) {
            throw new NullPointerException ("Engine may not be null"); //NOI18N
        }
        this.engine = engine;
    }
    
    protected final AbstractEngine getEngine() {
        return engine;
    }
    
    private String munge(String containerCtx) {
        return "toolbar" + "." + containerCtx; //NOI18N
    }
    
    private Map mappings = new HashMap();
    private void addMapping(String containerCtx, JComponent jc) {
        mappings.put (munge(containerCtx), jc);
    }
    
    private void removeMapping (String containerCtx) {
        mappings.remove (munge(containerCtx));
    }
    
    private JToolBar getToolbarForContext (String containerCtx) {
        return (JToolBar) mappings.get(munge(containerCtx));
    }
    
    public JToolBar createToolbar(String containerCtx) {
        JToolBar result = new JToolBar();
        result.setName(containerCtx);
        result.putClientProperty (KEY_CONTAINERCTX, containerCtx);
        result.putClientProperty (KEY_CREATOR, this);
        attachToToolbar(containerCtx, result);
        populateToolbar(containerCtx, result); //XXX listener should do this
        return result;
    }
    
    private void attachToToolbar (String containerCtx, JToolBar toolbar) {
        toolbar.addComponentListener (getToolbarListener());
        toolbar.addContainerListener (getToolbarListener());
    }
    
    private void detachFromToolbar (String containerCtx, JToolBar toolbar) {
        toolbar.removeComponentListener (getToolbarListener());
        toolbar.removeContainerListener (getToolbarListener());
        mappings.remove (containerCtx);
    }
    
    private Listener toolbarListener = null;
    private Listener getToolbarListener() {
        if (toolbarListener == null) {
            toolbarListener = new Listener();
        }
        return toolbarListener;
    }

    protected AbstractButton getOrCreateToolbarButton(int type) {
        AbstractButton result;
        if (type == ActionProvider.ACTION_TYPE_TOGGLE) {
            result = new JToggleButton();
        } else if (type == ActionProvider.ACTION_TYPE_ITEM) {
            result = new JButton();
        } else if (type == ActionProvider.ACTION_TYPE_SUBCONTEXT) {
            result = new JMenu();
        } else {
            result = null;
        }
        if (result != null) {
            result.addActionListener(getToolbarButtonListener());
        }
        //Otherwise presumably a wrapper or subclass will produce the result
        return result;
    }
    
    public void update (String containerCtx) {
//        System.err.println("ToolbarFactory update " + containerCtx);
        JToolBar tb = (JToolBar) mappings.get(munge(containerCtx));
        synchronized (tb.getTreeLock()) {
    //        System.err.println("Toolbar to update: " + tb);
            ActionProvider provider = getEngine().getActionProvider();
            if (tb != null) {
                Component[] c = tb.getComponents();
                for (int i=0; i < c.length; i++) {
                    if (c[i] instanceof AbstractButton) {
                        AbstractButton b = (AbstractButton) c[i];
                        String action = (String) b.getClientProperty (KEY_ACTION);
                        configureToolbarButton (b, containerCtx, action, provider,
                            getEngine().getContextProvider().getContext());
                    }
                }

            } else {
                System.err.println("Asked to update non existent toolbar " + containerCtx);
            }
        }
    }
    
    protected void populateToolbar (String containerCtx, JToolBar toolbar) {
//        System.err.println("AbstractToolbarFactory.populateToolbar");
        ActionProvider provider = getEngine().getActionProvider();
        String[] names = provider.getActionNames(containerCtx);
///        System.err.println("Names are " + Arrays.asList(names));
        for (int i=0; i < names.length; i++) {
            int type = provider.getActionType(names[i], containerCtx);
            AbstractButton item = getOrCreateToolbarButton(type);
            configureToolbarButton (item, containerCtx, names[i], provider, null);
            toolbar.add(item);
        }
        addMapping (containerCtx, toolbar);
        getEngine().notifyToolbarShown(containerCtx, toolbar); //XXX listener should do this
    }
    
    private void configureToolbarButton (AbstractButton item, String containerCtx, String action, ActionProvider provider, Map ctx) {
        item.setFocusable(false);
        item.setName(action);
        item.putClientProperty (KEY_ACTION, action);
        item.setToolTipText(
            provider.getDisplayName(action, containerCtx));
//        item.setToolTipText(provider.getDescription(action, containerCtx));
        int state = ctx == null ? ActionProvider.STATE_VISIBLE :
            provider.getState (action, containerCtx, ctx);
        boolean enabled = (state & ActionProvider.STATE_ENABLED) != 0;
        item.setEnabled(enabled);
        boolean visible = (state & ActionProvider.STATE_VISIBLE) != 0;
        item.setVisible (visible);
        boolean toggled = (state & ActionProvider.STATE_SELECTED) != 0;
        item.setSelected(toggled);
//        item.setMnemonic(provider.getMnemonic(action, containerCtx));
//        item.setDisplayedMnemonicIndex(provider.getMnemonicIndex(action, containerCtx));
        item.setIcon(provider.getIcon(action, containerCtx, BeanInfo.ICON_COLOR_16x16));
    }
    
    protected void depopulateToolbar (String containerCtx, JToolBar toolbar) {
        toolbar.removeAll();
        detachFromToolbar(containerCtx, toolbar);
    }
    
    private ButtonListener blistener = null;
    private ButtonListener getToolbarButtonListener() {
        if (blistener == null) {
            blistener = new ButtonListener();
        }
        return blistener;
    }
    
    private class ButtonListener implements ActionListener {
        public void actionPerformed (ActionEvent ae) {
            JComponent item = (JComponent) ae.getSource();
            String actionCommand = (String) item.getClientProperty(KEY_ACTION);
            String context = (String) item.getClientProperty(KEY_CONTAINERCTX);
            
            getEngine().notifyWillPerform (actionCommand, context);
            
            Action action = getEngine().getAction(context, actionCommand); 
            
            if (action.isEnabled()) {
                ActionEvent event = new ActionEvent (item, 
                ActionEvent.ACTION_PERFORMED, actionCommand);
                action.actionPerformed(event);
            }
            
            getEngine().notifyPerformed (actionCommand, context);
        }
    }
    
    private class Listener extends ComponentAdapter implements ContainerListener {
        
        public void componentAdded(ContainerEvent e) {
            JComponent jc = (JComponent) e.getChild();
            JToolBar tb = (JToolBar) e.getContainer();
            String ctx = (String) tb.getClientProperty(KEY_CONTAINERCTX);
            jc.putClientProperty (KEY_CONTAINERCTX, ctx);
        }
        
        public void componentHidden(ComponentEvent e) {
            JToolBar jtb = (JToolBar) e.getComponent();
            String ctx = (String) jtb.getClientProperty(KEY_CONTAINERCTX);
            depopulateToolbar (ctx, jtb);
        }
        
        public void componentRemoved(ContainerEvent e) {
            JComponent jc = (JComponent) e.getChild();
            jc.putClientProperty (KEY_CONTAINERCTX, null);
        }
        
        public void componentShown(ComponentEvent e) {
            JToolBar jtb = (JToolBar) e.getComponent();
            String ctx = (String) jtb.getClientProperty(KEY_CONTAINERCTX);
            populateToolbar (ctx, jtb);
            getEngine().notifyToolbarShown(ctx, jtb);
        }
        
    }
    
}
