/*
 * ICESOFT COMMERCIAL SOURCE CODE LICENSE V 1.1
 *
 * The contents of this file are subject to the ICEsoft Commercial Source
 * Code License Agreement V1.1 (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.icesoft.com/license/commercial-source-v1.1.html
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations under
 * the License.
 *
 * Copyright 2009-2015 ICEsoft Technologies Canada, Corp. All Rights Reserved.
 */
package com.icesoft.faces.facelets.component.selectiontree;

import javax.faces.event.ActionEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;


/**
 * Represents the tree node for the a SelectionTree
 *
 * @author philip.breau@icesoft.com
 */
public class SelectionTreeNode extends DefaultMutableTreeNode {

    private static final long serialVersionUID = -3062726468000880937L;

    /**
     * @return A list of all child tree nodes of this node
     */
    public List getChildren() {
        List list = new ArrayList();
        Enumeration enumer = super.children();
        while (enumer.hasMoreElements()) {
            list.add(enumer.nextElement());
        }
        return list;
    }

    /**
     * @return The count of child nodes having a TriStateCheckbox with a checked state
     */
    public int getCheckedChildrenCount() {
        int count = 0;
        for (Object o : getChildren()) {
            SelectionTreeNode child = (SelectionTreeNode) o;
            if (((SelectionTreeNodeUserObject) child.getUserObject()).getCheckbox().isChecked()) {
                count++;
            }
        }
        return count;
    }

    /**
     * @return The count of child nodes having a TriStateCheckbox with a checked state
     */
    public int getPartiallyCheckedChildrenCount() {
        int count = 0;
        for (Object o : getChildren()) {
            SelectionTreeNode child = (SelectionTreeNode) o;
            if (((SelectionTreeNodeUserObject) child.getUserObject()).getCheckbox().isPartiallyChecked()) {
                count++;
            }
        }
        return count;
    }


    /**
     * JSF actionListener for the <ice-cc:triStateCheckboxTree> node check event.
     * This event will update the state of the associated TriStateCheckbox and also
     * the states of all ascendent and descendant nodes.
     *
     * @param event The JSF event
     */
    public void checkboxClicked(ActionEvent event) {
        ((SelectionTreeNodeUserObject) getUserObject()).getCheckbox().checkboxClicked(event);
        if (((SelectionTreeNodeUserObject) getUserObject()).getCheckbox().isChecked()) {
            partiallyCheckAllAncestors();
            checkAllDescendents();
            checkParentIfAllSiblingsChecked(this);
        } else {
            this.uncheckAllDescendents();
            SelectionTreeNode parent = (SelectionTreeNode) this.getParent();
            while (parent != null) {
                parent.setPartiallySelectedIfCheckedDescendent();
                parent = (SelectionTreeNode) parent.getParent();
            }
        }
    }

    /**
     * Set this SelectionTree node to partially checked if there
     * are any descendants with a checked state.
     */
    public void setPartiallySelectedIfCheckedDescendent() {
        if (this.getCheckedChildrenCount() > 0 || this.getPartiallyCheckedChildrenCount() > 0) {
            ((SelectionTreeNodeUserObject) getUserObject()).getCheckbox().setPartiallyChecked();
        } else {
            ((SelectionTreeNodeUserObject) getUserObject()).getCheckbox().setUnchecked();
        }
    }

    private void partiallyCheckAllAncestors() {
        SelectionTreeNode parent = (SelectionTreeNode) this.getParent();
        while (parent != null) {
            if (((SelectionTreeNodeUserObject) parent.getUserObject()).getCheckbox().isChecked()) {
                break;
            }
            ((SelectionTreeNodeUserObject) parent.getUserObject()).getCheckbox().setPartiallyChecked();
            parent = (SelectionTreeNode) parent.getParent();
        }
    }


    /**
     * Set all SelectionTree descendant nodes to a checked state
     */
    public void checkAllDescendents() {
        checkAllDescendents(this);
    }

    private void checkAllDescendents(SelectionTreeNode node) {
        if (node.getChildCount() > 0) {
            for (Object o : node.getChildren()) {
                SelectionTreeNode child = (SelectionTreeNode) o;
                ((SelectionTreeNodeUserObject) child.getUserObject()).getCheckbox().setChecked();
                checkAllDescendents(child);
            }
        }
    }

    private void checkParentIfAllSiblingsChecked(SelectionTreeNode node) {
        SelectionTreeNode parent = (SelectionTreeNode) node.getParent();
        boolean siblingUnchecked = false;
        if (parent != null) {
            List siblings = parent.getChildren();
            for (Object sibling1 : siblings) {
                SelectionTreeNode sibling = (SelectionTreeNode) sibling1;
                if (!((SelectionTreeNodeUserObject) sibling.getUserObject()).getCheckbox().isChecked()) {
                    siblingUnchecked = true;
                    break;
                }
            }
            if (!siblingUnchecked) {
                ((SelectionTreeNodeUserObject) parent.getUserObject()).getCheckbox().setChecked();
            }
        }
    }

    /**
     * Set all SelectionTree descendant nodes to an unchecked state
     */
    public void uncheckAllDescendents() {
        uncheckAllDescendents(this);
    }

    private void uncheckAllDescendents(SelectionTreeNode node) {
        if (node.getChildCount() > 0) {
            for (Object o : node.getChildren()) {
                SelectionTreeNode child = (SelectionTreeNode) o;
                ((SelectionTreeNodeUserObject) child.getUserObject()).getCheckbox().setUnchecked();
                uncheckAllDescendents(child);
            }
        }
    }

}

