// 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.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * TODO comment
 * 
 * @version $Revision: $ $Date: 14.08.2012$
 * @author 2012, last modified by $Author: pharms$
 */
public class GUIModel<T extends IGUIElement> {
    
    /**
     * 
     */
    private Collection<T> guiElements = new ArrayList<T>();

    /**
     * 
     */
    private Map<T, List<T>> childRelations = new HashMap<T, List<T>>();

    /**
     * TODO: comment
     *
     * @param currentGUIElementPath
     * @return
     * @throws GUIModelException 
     */
    public T checkAndIntegratePath(List<T> guiElementPath) throws GUIModelException {
        T existingGuiElement = check(guiElementPath);
        
        if (existingGuiElement == null) {
            T terminalGuiElement = null;
            T child = null;
            boolean doBreak = false;
            for (int i = guiElementPath.size() - 1; ((i >= 0) && (!doBreak)); i--) {
                T newElement = guiElementPath.get(i);
                existingGuiElement = null;
                
                for (T candidate : guiElements) {
                    if (candidate.equals(newElement)) {
                        existingGuiElement = candidate;
                        break;
                    }
                }
                
                // element not yet contained in model. Add it.
                if (existingGuiElement == null)
                {
                    guiElements.add(newElement);
                    existingGuiElement = newElement;
                }
                else
                {
                    doBreak = true;
                }
                
                if (terminalGuiElement == null) {
                    terminalGuiElement = existingGuiElement;
                }
                
                if (child != null) {
                    List<T> children = childRelations.get(existingGuiElement);
                    if (children == null) {
                        children = new ArrayList<T>();
                        childRelations.put(existingGuiElement, children);
                    }
                    children.add(child);
                }
                child = existingGuiElement;
            }
            
            existingGuiElement = terminalGuiElement;
        }
        
        return existingGuiElement;
    }

    /**
     * TODO: comment
     *
     * @param root
     * @return
     */
    public List<T> getChildren(T guiElement) {
        return childRelations.get(guiElement);
    }

    /**
     * TODO: comment
     *
     * @return
     */
    public List<T> getRootElements() {
        List<T> roots = new ArrayList<T>();
        for (T guiElement : guiElements) {
            if (guiElement.getParent() == null) {
                roots.add(guiElement);
            }
        }
        return roots;
    }

    /**
     * TODO: comment
     *
     * @param guiElementPath
     * @throws GUIModelException 
     */
    private T check(List<T> guiElementPath) throws GUIModelException {
        T guiElementInModel = null;
        
        for (int i = 0; i < guiElementPath.size(); i++) {
            guiElementInModel = null;
            
            T newElement = guiElementPath.get(i);
            T newParent = (i > 0 ? guiElementPath.get(i - 1) : null);
            
            if ((newElement.getParent() != null) &&
                (!newElement.getParent().equals(newParent)))
            {
                throw new GUIModelException
                    ("new GUI element " + newElement + " denotes a parent element (" +
                     newElement.getParent() + ") which is not identical to the parent element " +
                     "denoted by the element path (" + newParent + ")");
            }
            
            for (T existingElement : guiElements) {
                if (existingElement.equals(newElement)) {
                    if (guiElementInModel != null) {
                        throw new GUIModelException
                            ("several GUI elements already existing in the model pretend to " +
                             "match the new element " + newElement);
                    }
                    
                    if ((existingElement.getParent() != newParent) &&
                        ((existingElement.getParent() != null) &&
                         (!existingElement.getParent().equals(newParent))))
                    {
                        throw new GUIModelException
                            ("the new path denotes the GUI element " + newElement +
                             " with parent " + newElement.getParent() + " that already exists in " +
                             "the model with a distinct parent element (" +
                             existingElement.getParent() + ")");
                    }
                    
                    guiElementInModel = existingElement;
                }
            }
        }
        
        return guiElementInModel;
    }

}
