/*
 * 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.apache.jmeter.control;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.swing.tree.TreeNode;

import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.gui.tree.JMeterTreeNode;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.JMeterProperty;
import org.apache.jmeter.testelement.property.NullProperty;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;

/**
 * The goal of ModuleController is to add modularity to JMeter. The general idea
 * is that web applications consist of small units of functionality (i.e. Logon,
 * Create Account, Logoff...) which consist of requests that implement the
 * functionality. These small units of functionality can be stored in
 * SimpleControllers as modules that can be linked together quickly to form
 * tests. ModuleController facilitates this by acting as a pointer to any
 * controller that sits under the WorkBench. The controller and it's subelements
 * will be substituted in place of the ModuleController at runtime. Config
 * elements can be attached to the ModuleController to alter the functionality
 * (which user logs in, which account is created, etc.) of the module.
 *
 */
public class ModuleController extends GenericController implements ReplaceableController {

    private static final String NODE_PATH = "ModuleController.node_path";// $NON-NLS-1$

    private JMeterTreeNode selectedNode = null;

    /**
     * No-arg constructor
     *
     * @see java.lang.Object#Object()
     */
    public ModuleController() {
        super();
    }

    public Object clone() {
        ModuleController clone = (ModuleController) super.clone();
        if (selectedNode == null) {
            this.restoreSelected();
        }
        clone.selectedNode = selectedNode;
        return clone;
    }

    /**
     * Sets the (@link JMeterTreeNode) which represents the controller which
     * this object is pointing to. Used for building the test case upon
     * execution.
     *
     * @param tn
     *            JMeterTreeNode
     * @see org.apache.jmeter.gui.tree.JMeterTreeNode
     */
    public void setSelectedNode(JMeterTreeNode tn) {
        selectedNode = tn;
        setNodePath();
    }

    /**
     * Gets the (@link JMeterTreeNode) for the Controller
     *
     * @return JMeterTreeNode
     */
    public JMeterTreeNode getSelectedNode() {
        if (selectedNode == null){
            restoreSelected();
        }
        return selectedNode;
    }

    private void setNodePath() {
        List nodePath = new ArrayList();
        if (selectedNode != null) {
            TreeNode[] path = selectedNode.getPath();
            for (int i = 0; i < path.length; i++) {
                nodePath.add(((JMeterTreeNode) path[i]).getName());
            }
            // nodePath.add(selectedNode.getName());
        }
        setProperty(new CollectionProperty(NODE_PATH, nodePath));
    }

    public List getNodePath() {
        JMeterProperty prop = getProperty(NODE_PATH);
        if (!(prop instanceof NullProperty)) {
            return (List) ((CollectionProperty) prop).getObjectValue();
        }
        return null;
    }

    private void restoreSelected() {
        GuiPackage gp = GuiPackage.getInstance();
        if (gp != null) {
            JMeterTreeNode root = (JMeterTreeNode) gp.getTreeModel().getRoot();
            resolveReplacementSubTree(root);
        }
    }

    /**
     * Compute the replacement tree.
     * @param context
     */
    public void resolveReplacementSubTree(JMeterTreeNode context) {
        if (selectedNode == null) {
            List nodePathList = getNodePath();
            if (nodePathList != null && nodePathList.size() > 0) {
                traverse(context, nodePathList, 1);
            }
        }
    }

    private void traverse(JMeterTreeNode node, List nodePath, int level) {
        if (node != null && nodePath.size() > level) {
            for (int i = 0; i < node.getChildCount(); i++) {
                JMeterTreeNode cur = (JMeterTreeNode) node.getChildAt(i);
                if (cur.getName().equals(nodePath.get(level).toString())) {
                    if (nodePath.size() == (level + 1)) {
                        selectedNode = cur;
                    }
                    traverse(cur, nodePath, level + 1);
                }
            }
        }
    }

    /**
     * Copies the controller's subelements into the execution tree
     *
     */
    public HashTree getReplacementSubTree() {
        HashTree tree = new ListedHashTree();
        if (selectedNode != null) {
            if (!selectedNode.isEnabled()) {
                selectedNode = cloneTreeNode(selectedNode);
                selectedNode.setEnabled(true);
            }
            tree.add(selectedNode);
            createSubTree(tree, selectedNode);
        }
        return tree;
    }

    private void createSubTree(HashTree tree, JMeterTreeNode node) {
        Enumeration e = node.children();
        while (e.hasMoreElements()) {
            JMeterTreeNode subNode = (JMeterTreeNode) e.nextElement();
            tree.add(subNode);
            createSubTree(tree.getTree(subNode), subNode);
        }
    }

    private static JMeterTreeNode cloneTreeNode(JMeterTreeNode node) {
        JMeterTreeNode treeNode = (JMeterTreeNode) node.clone();
        treeNode.setUserObject(((TestElement) node.getUserObject()).clone());
        cloneChildren(treeNode, node);
        return treeNode;
    }

    private static void cloneChildren(JMeterTreeNode to, JMeterTreeNode from) {
        Enumeration enumr = from.children();
        while (enumr.hasMoreElements()) {
            JMeterTreeNode child = (JMeterTreeNode) enumr.nextElement();
            JMeterTreeNode childClone = (JMeterTreeNode) child.clone();
            childClone.setUserObject(((TestElement) child.getUserObject()).clone());
            to.add(childClone);
            cloneChildren((JMeterTreeNode) to.getLastChild(), child);
        }
    }

}

@@ -2744,24 +2744,74 @@
 electedNode;
+ // TODO ?? (JMeterTreeNode) selectedNode.clone();
 %0A        ret

Add TODO