/*
 * 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.
 */

package org.netbeans.modules.beans;

import java.awt.Image;
import java.awt.datatransfer.Transferable;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;

import javax.swing.Action;
import org.openide.nodes.*;
import org.openide.util.actions.SystemAction;

/** Superclass of nodes representing bean patterns.
*
* @author Petr Hrebejk
*/


public class PatternNode extends AbstractNode {
    
    /** Array of the actions of the java methods, constructors and fields. */
    private static final SystemAction[] DEFAULT_ACTIONS = new SystemAction[] {
                /*
                SystemAction.get(OpenAction.class),
                null,
                SystemAction.get(CutAction.class),
                SystemAction.get(CopyAction.class),
                null,
                SystemAction.get(DeleteAction.class),
                SystemAction.get(RenameAction.class),
                null,
                //SystemAction.get(ToolsAction.class),
                SystemAction.get(PropertiesAction.class),
                */
            };


    /** Associated pattern. */
    protected Pattern pattern;

    /** Is this node read-only or are modifications permitted? */
    protected boolean writeable;

    /** Create a new pattern node.
    *
    * @param pattern pattern to represent
    * @param children child nodes
    * @param writeable <code>true</code> if this node should allow modifications.
    *        These include writable properties, clipboard operations, deletions, etc.
    */
    public PatternNode(Pattern pattern, Children children, boolean writeable) {
        super(children);
        this.pattern = pattern;
        this.writeable = writeable;
        
//        setActions(DEFAULT_ACTIONS);

        //this.pattern.addPropertyChangeListener(new WeakListeners.PropertyChange (this));
//        this.pattern.addPropertyChangeListener( WeakListeners.propertyChange (this, this.pattern));
        displayFormat = null;
    }
    
    public PatternNode( ClassPattern cp, boolean writeable ) {
        super(new PatternChildren(cp.getPatterns()));
        this.writeable = writeable;
        this.pattern = cp;
    }

    public Pattern getPattern() {
        return pattern;
    }

    @Override
    public Image getIcon(int type) {
        return pattern.getIcon();
    }

    @Override
    public Image getOpenedIcon(int type) {
        return pattern.getIcon();
    }

    @Override
    public String getDisplayName() {
        return pattern.getName();
    }

    @Override
    public String getHtmlDisplayName() {
        return pattern.getHtmlDisplayName();
    }
    
    /* Gets the short description of this node.
    * @return A localized short description associated with this node.
    */
    public String getShortDescription() {
        return super.getShortDescription(); // If not ovewloaded in ancestors
    }

//    public javax.swing.Action getPreferredAction() {
//        return SystemAction.get(OpenAction.class);
//    }
    
    /** Test whether this node can be renamed.
    * The default implementation assumes it can if this node is {@link #writeable}.
    *
    * @return <code>true</code> if this node can be renamed
    */
    @Override
    public boolean canRename() {
        return writeable;
    }

    /** Test whether this node can be deleted.
    * The default implementation assumes it can if this node is {@link #writeable}.
    *
    * @return <code>true</code> if this node can be renamed
    */
    @Override
    public boolean canDestroy () {
        return writeable;
    }

    /* Copy this node to the clipboard.
    *
    * @return {@link ExTransferable.Single} with one flavor, {@link NodeTransfer#nodeCopyFlavor}
    * @throws IOException if it could not copy
    */
    @Override
    public Transferable clipboardCopy () throws IOException {
        //PENDING
        return super.clipboardCopy();
    }

    /* Cut this node to the clipboard.
    *
    * @return {@link ExTransferable.Single} with one flavor, {@link NodeTransfer#nodeCopyFlavor}
    * @throws IOException if it could not cut
    */
    @Override
    public Transferable clipboardCut () throws IOException {
        if (!writeable)
            throw new IOException();

        //PENDING
        return super.clipboardCopy();
    }

    /** Test whether this node can be copied.
    * The default implementation returns <code>true</code>.
    * @return <code>true</code> if it can
    */
    @Override
    public boolean canCopy () {
        return false;
    }

    /** Test whether this node can be cut.
    * The default implementation assumes it can if this node is {@link #writeable}.
    * @return <code>true</code> if it can
    */
    @Override
    public boolean canCut () {
        return writeable;
    }

    @Override
    public Action[] getActions(boolean context) {
        return DEFAULT_ACTIONS;
    }

//    /** Set all actions for this node.
//    * @param actions new list of actions
//    */
//    public void setActions(SystemAction[] actions) {
//        systemActions = actions;
//    }

    /** Sets the name of the node */
    @Override
    public final void setName( String name ) {
//        try {
//            BeanUtils.beginTrans(true);
//            boolean rollback = true;
//            try  finally {
//                pattern.patternAnalyser.setIgnore(false);
//                BeanUtils.endTrans(rollback);
//            }
//            
//            superSetName( name );
//            
//        } catch (JmiException e) {
//            ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, e);
//        }

    }

    /** Called when node name is changed */
    public final void superSetName(String name) {
        super.setName( name );
    }

    /** Set's the name of pattern. Must be defined in descendants
    */
    protected  void setPatternName(String name) {
        throw new UnsupportedOperationException();
    };

//    /** Create a node property representing the pattern's name.
//    * @param canW if <code>false</code>, property will be read-only
//    * @return the property.
//    */
//    protected Node.Property createNameProperty(boolean canW) {
//        return new PatternPropertySupport(PatternProperties.PROP_NAME, String.class, canW) {
//                   /** Gets the value */
//                   public Object getValue () {
//                       return ((Pattern)pattern).getName();
//                   }
//
//                   /** Sets the value */
//                   public void setValue(Object val) throws IllegalArgumentException,
//                       IllegalAccessException, InvocationTargetException {
//                       super.setValue(val);
//                       String str = (String) val;
//                       try {
//                           BeanUtils.beginTrans(true);
//                           boolean rollback = true;
//                           try  finally {
//                               pattern.patternAnalyser.setIgnore(false);
//                               BeanUtils.endTrans(rollback);
//                           }
//                       } catch (JmiException e) {
//                           throw new InvocationTargetException(e);
//                       } catch (ClassCastException e) {
//                           throw new IllegalArgumentException();
//                       }
//                       superSetName(str);
//                   }
//               };
//    }

    /** Called when the node has to be destroyed */
    public void destroy() throws IOException {
//        try {
//            BeanUtils.beginTrans(true);
//            boolean rollback = true;
//            try  finally {
//                BeanUtils.endTrans(rollback);
//            }
//        } catch (JmiException e) {
//            IOException ioe = new IOException();
//            ioe.initCause(e);
//            throw ioe;
//        }
//        super.destroy();
    }

//    protected static String getFormattedMethodName(Method method) {
//        String name = null;
//        Format fmt = SourceNodes.createElementFormat("{n} ({p})"); // NOI18N
//        try {
//            if (method != null) {
//                name = fmt.format (method);
//            }
//        } catch (IllegalArgumentException e) {
//            ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, e);
//        }
//
//        return name != null? name: PatternNode.getString("LAB_NoMethod"); // NOI18N
//    }

    public void updateRecursively( Pattern p ) {
        Children ch = getChildren();
        if ( ch instanceof PatternChildren ) {           
           HashSet<Pattern> oldSubs = new HashSet<Pattern>( p.getPatterns() );

           
           // Create a hashtable which maps Description to node.
           // We will then identify the nodes by the description. The trick is 
           // that the new and old description are equal and have the same hashcode
           Node[] nodes = ch.getNodes( true );           
           HashMap<Pattern,PatternNode> oldPattern2node = new HashMap<Pattern, PatternNode>();           
           for (Node node : nodes) {
               oldPattern2node.put(((PatternNode)node).pattern, (PatternNode)node);
           }
           
           // Now refresh keys
           ((PatternChildren)ch).resetKeys(p.getPatterns() /*, pattern.ui.getFilters() */);

           
           // Reread nodes
           nodes = ch.getNodes( true );
           
           for( Pattern newSub : p.getPatterns() ) {
           //for( Node newNode : nodes) {
                //Pattern newSub = ((PatternNode)newNode).pattern;
                PatternNode node = oldPattern2node.get(newSub);
                if ( node != null ) { // filtered out
                    if ( !oldSubs.contains(newSub) && node.getChildren() != Children.LEAF) {                                           
                        pattern.getPatternAnalyser().getUI().expandNode(node); // Make sure new nodes get expanded
                    }     
                    ((PatternNode)node).updateRecursively( newSub ); // update the node recursively
                }
           }
        }
                        
        Pattern oldPattern = pattern; // Remember old description        
        pattern = p; // set new descrioption to the new node

        // XXXX 

        
        if ( oldPattern.getHtmlDisplayName() != null && !oldPattern.getHtmlDisplayName().equals(pattern.getHtmlDisplayName())) {
            fireDisplayNameChange(oldPattern.name, pattern.name);
        }
          if( oldPattern.getIcon() != null &&  oldPattern.getIcon() != pattern.getIcon()) {
            fireIconChange();
            fireOpenedIconChange();
        }
    }

}
