// Module    : $RCSfile: GUIModel.java,v $
// Version   : $Revision: 0.0 $  $Author: pharms $  $Date: 14.08.2012 $
// Project   : quest-core-events
// Creation  : 2012 by pharms
// Copyright : Patrick Harms, 2012

package de.ugoe.cs.quest.eventcore.guimodel;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * <p>
 * The goal of a GUI model is to correctly l
 * </p>
 * 
 * @version $Revision: $ $Date: 14.08.2012$
 * @author 2012, last modified by $Author: pharms$
 */
public class GUIModel {
    
    /**
     * 
     */
    private TreeNode root = new TreeNode();
    
    /**
     * 
     */
    private List<TreeNode> allNodes = new ArrayList<TreeNode>();

    /**
     * TODO: comment
     *
     * @param currentGUIElementPath
     * @return
     * @throws GUIModelException 
     */
    public IGUIElement integratePath(List<? extends IGUIElementSpec> guiElementPath,
                                     IGUIElementFactory              guiElementFactory)
        throws GUIModelException
    {
        List<IGUIElementSpec> remainingPath = new LinkedList<IGUIElementSpec>();
        
        for (IGUIElementSpec spec : guiElementPath)
        {
            remainingPath.add(spec);
        }
        
        return integratePath(root, remainingPath, guiElementFactory);
    }

    /**
     * TODO: comment
     *
     * @param root
     * @return
     */
    public List<IGUIElement> getChildren(IGUIElement guiElement) {
        for (TreeNode node : allNodes) {
            if (node.guiElement.equals(guiElement)) {
                List<IGUIElement> result = new ArrayList<IGUIElement>();
                
                if (node.children != null) {
                    for (TreeNode child : node.children) {
                      result.add(child.guiElement);
                    }
                }
                
                return result;
            }
        }
        
        return null;
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     * @param guiElement
     * @return
     */
    public IGUIElement getParent(IGUIElement guiElement) {
        for (TreeNode node : allNodes) {
            for (TreeNode child : node.children) {
                if (child.guiElement.equals(guiElement)) {
                    return node.guiElement;
                }
            }
        }
        
        return null;
    }

    /**
     * TODO: comment
     *
     * @return
     */
    public List<IGUIElement> getRootElements() {
        List<IGUIElement> roots = new ArrayList<IGUIElement>();
        for (TreeNode rootChild : root.children) {
            roots.add(rootChild.guiElement);
        }
        return roots;
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     * @param root2
     * @param guiElementPath
     * @param guiElementFactory
     * @return
     * @throws GUIModelException 
     */
    private IGUIElement integratePath(TreeNode                        parentNode,
                                      List<? extends IGUIElementSpec> remainingPath,
                                      IGUIElementFactory              guiElementFactory)
        throws GUIModelException
    {
        IGUIElementSpec specToIntegrateElementFor = remainingPath.remove(0);
        
        List<TreeNode> matchingChildren = new ArrayList<TreeNode>();
        
        if (parentNode.children != null) {
            for (TreeNode child : parentNode.children) {
                if (specToIntegrateElementFor.getSimilarity(child.guiElement.getSpecification())) {
                    matchingChildren.add(child);
                }
            }
        }
        
        // if we get here, the corresponding path does not exist yet. So create it
        if (matchingChildren.size() == 0) {
            IGUIElement newElement = guiElementFactory.instantiateGUIElement
                (specToIntegrateElementFor, parentNode.guiElement);
            
            matchingChildren.add(parentNode.addChild(newElement));
        }
        else if (matchingChildren.size() > 1) {
            throw new GUIModelException
              ("several children of gui element " + parentNode.guiElement +
               " match the specification " + specToIntegrateElementFor + " at the same level. " +
               "Can not decide which is the right one.");
        }
        
        if (remainingPath.size() > 0) {
            matchingChildren.get(0).guiElement.updateSpecification(specToIntegrateElementFor);
            return integratePath(matchingChildren.get(0), remainingPath, guiElementFactory);
        }
        else {
            return matchingChildren.get(0).guiElement;
        }
    }

    /**
     * <p>
     * TODO comment
     * </p>
     * 
     * @version $Revision: $ $Date: 17.08.2012$
     * @author 2012, last modified by $Author: pharms$
     */
    private class TreeNode
    {
        /** */
        private IGUIElement guiElement;
        
        /** */
        private List<TreeNode> children;
        
        /** */
        //private TreeNode parent;
        
        /**
         * <p>
         * TODO: comment
         * </p>
         *
         * @param guiElement
         * @return
         */
        private TreeNode addChild(IGUIElement guiElement)
        {
            if (children == null)
            {
                children = new ArrayList<TreeNode>();
            }
            
            TreeNode child = new TreeNode();
            child.guiElement = guiElement;
            //child.parent = this;
            children.add(child);
            
            allNodes.add(child);
            
            return child;
        }
    }
}
