/*****************************************************************************
 *                                                                           *
 *  This file is part of the tna framework distribution.                     *
 *  Documentation and updates may be get from  biaoping.yin the author of    *
 *  this framework							     *
 *                                                                           *
 *  Sun Public License Notice:                                               *
 *                                                                           *
 *  The contents of this file are subject to the Sun Public License Version  *
 *  1.0 (the "License"); you may not use this file except in compliance with *
 *  the License. A copy of the License is available at http://www.sun.com    * 
 *                                                                           *
 *  The Original Code is tag. The Initial Developer of the Original          *    
 *  Code is biaoping yin. Portions created by biaoping yin are Copyright     *
 *  (C) 2000.  All Rights Reserved.                                          *
 *                                                                           *
 *  GNU Public License Notice:                                               *
 *                                                                           *
 *  Alternatively, the contents of this file may be used under the terms of  *
 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 *  provisions of LGPL are applicable instead of those above. If you wish to *
 *  allow use of your version of this file only under the  terms of the LGPL *
 *  and not to allow others to use your version of this file under the SPL,  *
 *  indicate your decision by deleting the provisions above and replace      *
 *  them with the notice and other provisions required by the LGPL.  If you  *
 *  do not delete the provisions above, a recipient may use your version of  *
 *  this file under either the SPL or the LGPL.                              *
 *                                                                           *
 *  biaoping.yin (yin-bp@163.com)                                            *
 *  Author of Learning Java 						     					 *
 *                                                                           *
 *****************************************************************************/



package com.frameworkset.common.tag.tree.impl;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import javax.servlet.http.HttpServletRequest;

import com.frameworkset.common.tag.contextmenu.ContextMenuImpl;
import com.frameworkset.common.tag.tree.itf.ICollapseListener;
import com.frameworkset.common.tag.tree.itf.IExpandListener;
import com.frameworkset.common.tag.tree.itf.ISelectListener;
import com.frameworkset.common.tag.tree.itf.ITree;
import com.frameworkset.common.tag.tree.itf.ITreeNode;
import com.frameworkset.common.tag.tree.itf.IUnselectListener;

/**
 * 定义树结构的抽象类
 * @author biaoping.yin
 * created on 2005-3-25
 * version 1.0
 */
public abstract class Tree extends ContextMenuImpl implements ITree,java.io.Serializable {
	/**
	 * level:定义默认展开层级
	 */
	protected int level = -1;
    protected boolean   singleSelectionMode = false;
    protected Set       expanded = new TreeSet();
    protected Set       selected = new TreeSet();
    protected ITreeNode root     = null;

    protected List      expandListeners   = new ArrayList();
    protected List      collapseListeners = new ArrayList();
    protected List      selectListeners   = new ArrayList();
    protected List      unselectListeners = new ArrayList();
	protected List      nodeListeners = new ArrayList();
	/**标识当前展开得节点*/
	protected ITreeNode curExpanded;
	
	protected boolean dynamic = true;
	
	
	protected boolean needObservable = false;
	//	added this attribute by biaoping.yin on 2005-02-04
	/**更新结点得信息*/
	protected boolean refreshNode = true;
	
	protected boolean uprecursive = false;
	
	protected boolean recursive = false;
	
	public static final String mode_static = "static";
    public static final String mode_static_dynamic = "static-dynamic";
    public static final String mode_dynamic = "dynamic";
	
	
	
	
	/**
	 * 指定是否对树节点进行排序,缺省为true
	 */
    private boolean sortable = false;
    
	
	public void setNeedObservable(boolean needObservable)
	{
		this.needObservable = needObservable;
	}	
	
	/**
	 * 判断是否需要注册到观察器中
	 * @return boolean
	 */
	protected boolean isNeedObservable()
	{
		return this.needObservable;
	}

    public ITreeNode getRoot() {
        return this.root;
        
    }

    public void setRoot(ITreeNode node) {
    	
        this.root = node;
        this.root.setTree(this);
    }

    public ITreeNode findNode(String treeNodeId) {
        return findNode(getRoot(), treeNodeId);
    }

    protected ITreeNode findNode(ITreeNode treeNode, String treeNodeId){
        if(treeNode.getId().equals(treeNodeId)){
            return treeNode;
        }

        Iterator children = treeNode.getChildren().iterator();
        while(children.hasNext()){
            ITreeNode child = (ITreeNode) children.next();
            ITreeNode match = findNode(child, treeNodeId);
            if( match != null){
                return match;
            }
        }
        return null;
    }

    public Set findNodes(Set treeNodeIds) {
        Set treeNodes = new HashSet();
        findNodes(getRoot(), treeNodeIds, treeNodes);
        return treeNodes;
    }

    protected void findNodes(ITreeNode treeNode, Set treeNodeIds, Set treeNodes){
        if(treeNodeIds.contains(treeNode.getId())){
            treeNodes.add(treeNode);
        }

        Iterator children = treeNode.getChildren().iterator();
        while(children.hasNext()){
            findNodes((ITreeNode) children.next(), treeNodeIds, treeNodes);
        }
    }

    public boolean isExpanded(String treeNodeId){
        return this.expanded.contains(treeNodeId);
    }
	
//	/**
//	 * 展开节点treeNodeId，curLevel为当前层级
//	 */
//	
//    public void expand(String treeNodeId,int curLevel) {
//        this.expanded.add(treeNodeId);
//		ITreeNode expandedNode = findNode(treeNodeId);
//		/**
//		 * 如果需要实时刷新所有节点的信息，则调用notifyObservers方法通知所有的其他的节点
//		 * 及时刷新信息
//		 */
//		System.out.println("expand treenodeID 2:" + treeNodeId);
//		if (needObservable)
//		{
//			notifyObservers(expandedNode);
//		}		
//        if(this.expandListeners.size() > 0){
//            
//            
//            /**
//			if(expandedNode == null)
//			{
//				System.out.println("expandedNode:null");
//				expanded.remove(treeNodeId);
//				return;
//			}
//			*/
//			
//            Iterator iterator = this.expandListeners.iterator();
//            while(iterator.hasNext()){
//                ((IExpandListener) iterator.next()).nodeExpanded(expandedNode, this,curLevel,needObservable);
//            }            
//        }
//    }
    
    /**
     * 
     *  Description:
     * @param expandedNode
     * @param curLevel
     * @see com.frameworkset.common.tag.tree.itf.ITree#expand(com.frameworkset.common.tag.tree.itf.ITreeNode, int)
     */
	public void expand(ITreeNode expandedNode,int curLevel) {
		this.expanded.add(expandedNode.getId());
		//当展开事件是由于手动即点击页面的展开图标触发时，广播该事件
		if (needObservable && curLevel > level)
		{
			notifyObservers(expandedNode);
		}
		if(this.expandListeners.size() > 0){		
		Iterator iterator = this.expandListeners.iterator();
		while(iterator.hasNext()){
				((IExpandListener) iterator.next()).nodeExpanded(expandedNode, this,curLevel,needObservable);
			}
		}		
	}
	
	
    /**
     *  静态模式时使用
     *  Description:
     * @param expandedNode
     * @param curLevel
     * @see com.frameworkset.common.tag.tree.itf.ITree#expand(com.frameworkset.common.tag.tree.itf.ITreeNode, int)
     */
	public void impactExpand(ITreeNode expandedNode,int curLevel) {
//		this.expanded.add(expandedNode.getId());
		//当展开事件是由于手动即点击页面的展开图标触发时，广播该事件
		if (needObservable && curLevel > level)
		{
			notifyObservers(expandedNode);
		}
		if(this.expandListeners.size() > 0){		
		Iterator iterator = this.expandListeners.iterator();
		while(iterator.hasNext()){
				((IExpandListener) iterator.next()).impactExpandNode(expandedNode, this,curLevel,needObservable);
			}
		}		
	}
    
    /**
     * 页面上点击展开图标时，调用本方法展开点击的节点
     *  Description:
     * @param treeNodeId
     * @see com.frameworkset.common.tag.tree.itf.ITree#expand(java.lang.String)
     */
	public void expand(String treeNodeId,
					   String mode,
					   String scope,
					   HttpServletRequest request) {
		//modified by biaoping.yin on 2005-02-05
//		this.expanded.add(treeNodeId);
//		ITreeNode expandedNode = findNode(treeNodeId);	
//		if (needObservable)
//		{
//			notifyObservers(expandedNode);
//		}	
//		if(this.expandListeners.size() > 0){
//			Iterator iterator = this.expandListeners.iterator();
//			while(iterator.hasNext()){
//				((IExpandListener) iterator.next()).nodeExpanded(expandedNode, this,needObservable);
//			}
//		}
//		System.out.println("expand treenodeID 0:" + treeNodeId);
//		if(mode == null || mode.equals(""))
//		{
//			ITreeNode expandedNode = findNode(treeNodeId);
//			this.curExpanded = expandedNode;
//			expand(expandedNode,this.getUnknownLevel());
//		}
		if(mode.equals(Tree.mode_static_dynamic))
		{
//			 linkUrl += "__nodename=" + name;
//		      linkUrl += "__nodetype=" + nodetype;
//		      linkUrl += "__nodepath=" + nodepath;
			String treeName = request.getParameter("__nodename");
			String type = request.getParameter("__nodetype");
			String path = request.getParameter("__nodepath");
			String isfirstChild = request.getParameter("__nodefirst");
			String islastChild = request.getParameter("__nodelast");
			
			ITreeNode expandedNode =
	            new TreeNode(
	            	treeNodeId,
	                treeName,
	                type,
	                true,
	                null,
	                null,
	                null,
	                null, path, null);
			if(isfirstChild != null && !isfirstChild.equals("true"))
			{
				ITreeNode leftNode = new TreeNode();
				expandedNode.setLeftNode(leftNode);
			}
			
			if(islastChild != null && !islastChild.equals("true"))
			{
				ITreeNode lastNode = new TreeNode();
				expandedNode.setRightNode(lastNode);
			}
//			else
//				node =
//					new TreeNode(
//						treeid,
//						treeName,
//						type,
//						showHref,
//						this,
//						memo,
//						radioValue,
//						checkboxValue);
	        /**
	         * mark hasSon;
	         */
			expandedNode.setHasChildren(true);
			expandedNode.setTree(this);
	        this.curExpanded = expandedNode;
			expand(expandedNode,this.getUnknownLevel());
		}
		else
		{
			ITreeNode expandedNode = findNode(treeNodeId);
			this.curExpanded = expandedNode;
			expand(expandedNode,this.getUnknownLevel());
		}
	}
	
	/**
	 * 
	 *  Description:点击节点的折叠图标时调用本方法
	 * @param treeNodeId
	 * @see com.frameworkset.common.tag.tree.itf.ITree#collapse(java.lang.String)
	 */
    public void collapse(String treeNodeId) {
        this.expanded.remove(treeNodeId);
//        System.out.println("collapse treenodeID:" + treeNodeId);
//		System.out.println("treenodeID 4050 is expanded:" + this.isExpanded("4050"));
//		System.out.println("tree:" + this);
		
		ITreeNode collapsedNode = findNode(treeNodeId);		
		
		if (needObservable)
		{
			notifyObservers(collapsedNode);
		}
        if(this.collapseListeners.size() > 0){           
            
            Iterator iterator = this.collapseListeners.iterator();
            while(iterator.hasNext()){
                ((ICollapseListener) iterator.next()).nodeCollapsed(collapsedNode, this,needObservable);
            }
        }	
		//System.out.println("treenodeID 4050 is expanded:" + this.isExpanded("4050"));
    }
    
//	public void collapse(ITreeNode collapsedNode) {
//		this.expanded.remove(collapsedNode.getId());
//		if (needObservable)
//		{
//			notifyObservers(collapsedNode);
//		}
//		if(this.collapseListeners.size() > 0){
//			//ITreeNode collapsedNode = findNode(treeNodeId);
//			Iterator iterator = this.collapseListeners.iterator();
//			while(iterator.hasNext()){
//				((ICollapseListener) iterator.next()).nodeCollapsed(collapsedNode, this,needObservable);
//			}
//		}
//
//	}
    public Set getExpandedNodes() {
        return findNodes(this.expanded);
    }

    public void addExpandListener(IExpandListener expandListener) {
        this.expandListeners.add(expandListener);
    }

    public void removeExpandListener(IExpandListener expandListener) {
        this.expandListeners.remove(expandListener);
    }

    public void addCollapseListener(ICollapseListener collapseListener) {
        this.collapseListeners.add(collapseListener);
    }

    public void removeCollapseListener(ICollapseListener collapseListener) {
        this.collapseListeners.remove(collapseListener);
    }

    public boolean isSelected(String treeNodeId) {
        return this.selected.contains(treeNodeId);
    }

    public void select(String treeNodeId) {
        if(isSingleSelectionMode()){
            unSelectAll();
        }
		ITreeNode selectedNode = findNode(treeNodeId);
		
        this.selected.add(treeNodeId);
		if (needObservable)
		{
			notifyObservers(selectedNode);
		}

        if(this.selectListeners.size() > 0){
            
            
            Iterator iterator = this.selectListeners.iterator();
            while(iterator.hasNext()){
                ((ISelectListener) iterator.next()).nodeSelected(selectedNode, this,needObservable);
            }
        }
    }

    public void unSelect(String treeNodeId) {
        this.selected.remove(treeNodeId);
		ITreeNode unselectedNode = findNode(treeNodeId);
		if (needObservable)
		{
			notifyObservers(unselectedNode);
		}
        if(this.unselectListeners.size() > 0){			
            Iterator iterator = this.unselectListeners.iterator();
            while(iterator.hasNext()){
                ((IUnselectListener) iterator.next()).nodeUnselected(unselectedNode, this,needObservable);
            }
        }
    }
    
	public void unSelect(ITreeNode unselectedNode) {
		this.selected.remove(unselectedNode.getId());
		if (needObservable)
		{
			notifyObservers(unselectedNode);
		}
		if(this.unselectListeners.size() > 0){
			//ITreeNode unselectedNode = findNode(treeNodeId);
			Iterator iterator = this.unselectListeners.iterator();
			while(iterator.hasNext()){
				((IUnselectListener) iterator.next()).nodeUnselected(unselectedNode, this,needObservable);
			}
		}
		}


    public void unSelectAll() {
        Iterator iterator =  this.selected.iterator();
        while(iterator.hasNext()){
            unSelect((String) iterator.next());
        }
    }

    public Set getSelectedNodes() {
        return findNodes(this.selected);
    }

    public void setSingleSelectionMode(boolean mode) {
        this.singleSelectionMode = mode;
    }

    public boolean isSingleSelectionMode(){
        return this.singleSelectionMode;
    }
    
   

    public void addSelectListener(ISelectListener selectListener) {
        this.selectListeners.add(selectListener);
    }

    public void removeSelectListener(ISelectListener selectListener) {
        this.selectListeners.remove(selectListener);
    }

    public void addUnselectListener(IUnselectListener unselectListener) {
        this.unselectListeners.add(unselectListener);
    }

    public void removeUnselectListener(IUnselectListener unselectListener) {
        this.unselectListeners.remove(unselectListener);
    }

    public TreeIterator iterator(boolean includeRootNode) {
        return new TreeIterator(this, includeRootNode);
    }
    
    public TreeIterator iterator(String parentIndent) {
        return new TreeIterator(this, parentIndent);
    }
    
    /**
     * added by yinbiaoping on 2004/03/23
     * 重载方法java.util.Observable#notifyObservers(Object o)
     */
	public void notifyObservers(Object o)
	{
		setChanged();
		super.notifyObservers(o);		
	}    
	/**
	 * Description:
	 * @return
	 * boolean
	 */
	public boolean isRefreshNode() {
		return refreshNode;
	}

	/**
	 * Description:
	 * @param b
	 * void
	 */
	public void setRefreshNode(boolean b) {
		refreshNode = b;
	}
	
	/**
	 * 不知道当前层级时，调用本方法获取当前层级
	 * 没有指定节点所处的当前层级，为了避免与其他已有层级发生冲突，指定当前层级比默认层级大1，
	 * 这样setSon方法调用addNode方法不至于因为当前所处的层级
	 * 比默认展开层级小而展开其下级的情况出现
	 * Description:
	 * @return
	 * int
	 */
	protected int getUnknownLevel()
	{
		return level + 1;
	}
	
	public void setSortable(boolean b)
	{
		this.sortable = b;
		
	}


	public boolean isSortable()
	{
		return sortable;
	}

	/**
	 * 获取当前展开的节点信息，
	 * 主要在动静态方式生成树时当太地返回当前的树节点的html信息
	 */
	public ITreeNode getCurExpanded()
	{
//		return curExpanded == null ? this.root : curExpanded;
        return curExpanded ;
	}

	public boolean isDynamic()
	{
//		if(this.newmode)
			return this.mode.equals("dynamic");
//		return dynamic;
	}
	
	public boolean isStatic()
	{
//		if(this.newmode)
			return this.mode.equals("static");
//		else
//		{
//			return false;
//		}
	}
	
	public boolean isStaticDynamic()
	{
//		if(this.newmode)
			return this.mode.equals("static-dynamic");
//		else
//		{
//			return !this.dynamic;
//		}
	}

	public void setDynamic(boolean dynamic)
	{
		this.dynamic = dynamic;
	}
//	private boolean newmode = false;
	private String mode;
	private boolean partuprecursive = false;
	public void setMode(String mode)
	{
		
		this.mode = mode;
//		if(mode != null && !mode.trim().equals(""))
//			this.newmode = true;
	}

	public boolean isRecursive() {
		return recursive;
	}

	public void setRecursive(boolean recursive) {
		this.recursive = recursive;
	}

	public boolean isUprecursive() {
		return uprecursive;
	}

	public void setUprecursive(boolean uprecursive) {
		this.uprecursive = uprecursive;
	}
	
	
	public void setPartuprecursive(boolean partuprecursive)
	{
		this.partuprecursive  = partuprecursive;
	}
	
	public boolean isPartuprecursive()
	{
		return partuprecursive;
	}

}
