/**
 *  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.wicketTutorial.checkboxtree;
import java.util.Iterator;
import java.util.Set;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
import org.apache.wicket.extensions.markup.html.repeater.tree.content.CheckedFolder;
import org.apache.wicket.extensions.markup.html.repeater.tree.content.Folder;
import org.apache.wicket.extensions.markup.html.repeater.tree.nested.BranchItem;
import org.apache.wicket.extensions.model.AbstractCheckBoxModel;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.resource.PackageResourceReference;


public class AutocheckedFolder<T> extends CheckedFolder<T> {

    private ITreeProvider<T> treeProvider;
    private IModel<Set<T>> checkedNodes;
    private IModel<Boolean> checkboxModel;
    
    public AutocheckedFolder(String id, AbstractTree<T> tree, 
                             IModel<T> model, IModel<Set<T>> checkedNodes) {
        super(id, tree, model); 
        this.treeProvider = tree.getProvider();
        this.checkedNodes = checkedNodes;
        
        setOutputMarkupId(true);
    }
    
    @Override
    protected IModel<Boolean> newCheckBoxModel(IModel<T> model) {
        checkboxModel =  new CheckModel();
        return checkboxModel;
    }
    
    @Override
    protected void onUpdate(AjaxRequestTarget target) {
        super.onUpdate(target);
        T node = getModelObject();
        boolean nodeChecked = checkboxModel.getObject();
        
        addRemoveSubNodes(node, nodeChecked);               
        addRemoveAncestorNodes(node, nodeChecked);
        
        updateNodeOnClientSide(target, nodeChecked);        
    }

    protected void updateNodeOnClientSide(AjaxRequestTarget target,
            boolean nodeChecked) {
        target.appendJavaScript(";CheckAncestorsAndChildren.checkChildren('" + getMarkupId() + "'," 
                + nodeChecked + ");");
        
        target.appendJavaScript(";CheckAncestorsAndChildren.checkAncestors('" + getMarkupId() + "'," 
                + nodeChecked + ");");
    }
    
    @Override
    public void renderHead(IHeaderResponse response) {
        PackageResourceReference scriptFile = new PackageResourceReference(this.getClass(), "autocheckedFolder.js");
        response.render(JavaScriptHeaderItem.forReference(scriptFile));
    }
    
    protected void addRemoveSubNodes(T node, Boolean nodeStatus) {
        Iterator<? extends T> childrenNodes = treeProvider.getChildren(node);
        
        while (childrenNodes.hasNext()) {
            T childNode = childrenNodes.next();
            addRemoveSubNodes(childNode, nodeStatus);
        }
        
        if(nodeStatus)
            checkedNodes.getObject().add(node);
        else
            checkedNodes.getObject().remove(node);
    }
    
    protected void addRemoveAncestorNodes(T node, Boolean nodeStatus) {
        BranchItem currentConatiner = findParent(BranchItem.class);
        
        if(currentConatiner == null) {
            return;
        }
        
        BranchItem ancestor = currentConatiner.findParent(BranchItem.class);
        
        while(ancestor != null)
        {
            T nodeObject = (T) ancestor.getDefaultModelObject();
            
            if(nodeStatus)
            {
                checkedNodes.getObject().add(nodeObject);
            }
            else
            {
                if(!hasNodeCheckedChildren(nodeObject))
                    checkedNodes.getObject().remove(nodeObject);                
            }
            
            ancestor = ancestor.findParent(BranchItem.class);
        }
    }

    protected boolean hasNodeCheckedChildren(T nodeObject) {
        Iterator<? extends T> children = treeProvider.getChildren(nodeObject);
        
        while (children.hasNext()) {
            T child = children.next();
            if(checkedNodes.getObject().contains(child))
                return true;
        }
        
        return false;
    }

    class CheckModel extends AbstractCheckBoxModel{
        @Override
        public boolean isSelected() {
            return checkedNodes.getObject()
                    .contains(getModelObject());
        }

        @Override
        public void select() {
            checkedNodes.getObject().add(getModelObject());
        }

        @Override
        public void unselect() {
            checkedNodes.getObject().remove(getModelObject());
        }               
    }
}