/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed 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 com.thesett.aima.attribute.impl;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import com.thesett.aima.search.QueueBasedSearchMethod;
import com.thesett.aima.search.SearchNode;
import com.thesett.aima.search.SearchNotExhaustiveException;
import com.thesett.aima.search.util.TreeSearchState;
import com.thesett.aima.search.util.uninformed.DepthBoundedSearch;
import com.thesett.aima.state.BaseType;
import com.thesett.aima.state.InfiniteValuesException;
import com.thesett.aima.state.OrdinalAttribute;
import com.thesett.aima.state.ReferencableAttribute;
import com.thesett.aima.state.Type;
import com.thesett.aima.state.TypeVisitor;
import com.thesett.common.error.NotImplementedException;
import com.thesett.common.util.SimpleTree;
import com.thesett.common.util.Tree;
import com.thesett.common.util.logic.TrueUnaryPredicate;
import com.thesett.common.util.logic.UnaryPredicate;
import com.thesett.common.validate.Validation;

/**
 * HierarchyAttributes are properties that are instances of a path in a tree structured categorisation. An example is
 * the categorisation of life forms, the root is "all life", the next level splits into "plant", "animal", "bacteria",
 * "virus", at the next level "animal" splits into "mamal", "reptile", "fish", ... and so on. Actual instances of life
 * forms fit into this tree at appropriate places and the tree forms a way of selecting sub-categories of life forms at
 * different levels. Broader sub-categories exist further up the tree and more fine grained sub-categories are formed
 * further down. An individual instance of a life form categorisation is a path from the root of the tree down to the
 * appropriate sub-category.
 *
 *
In the life forms example, instances of life forms fit into the tree but only at certain allowable places. For
 * example all animals fit into one of the "animal" sub categories, so no animal can be placed into the tree directly at
 * the "animal" category level. Often only the leaves of a hierarchy tree will be allowable instances but in some cases
 * non-leaf categories may be allowable too. The number of possible values that an hierarchy attribute of a given type
 * can take on is always the number of allowable categories in the type, not the total number of sub-categories and not
 * the number of leaves in the tree.
 *
 *
Often hierarchies are balanced in the number of levels accross all instances; that is to say that the path
 * distance from the root to each allowable leaf is the same for each leaf. This does not have to be the case, however.
 *
 *
The category labels throughout the hierarchy tree will always be strings. The implementation enumerates these
 * strings and uses a more compact representation internally and provides faster equality checks between attribute
 * instances than would be obtained by doing string by string comparisons.
 *
 *
In addition to equality checks this data type also provides sub-category checks to test if one category is a
 * legitimate sub-category of another.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Represent a single allowable sub-category from a tree of hierarchical categories.
 *
 Convert a sub-category to a compact representation.
 *
 Restore a sub-category from a compact representation.
 *
 Provide a factory for creating hierarchy attributes.
 *
 Provide fast equality checking on sub-categories.
 *
 Provide fast sub-category checking.
 *

 *
 * @author Rupert Smith
 */
public class HierarchyAttribute implements OrdinalAttribute, ReferencableAttribute, Serializable
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(HierarchyAttribute.class.getName()); */

    /** Used to hold all the different named attribute classes. */
    private static Map<String, HierarchyClassImpl> attributeClasses = new HashMap<String, HierarchyClassImpl>();

    /**
     * Used to hold the compact int representation of the hierarchy attribute. The special value of -1 is used to
     * indicate that this attribute has not had any value assigned to it yet. Other negative values are used to
     * represent partial assignments that have not yet been completed.
     */
    int value;

    /** Holds a reference to the class of hierarchy attribute that this one belongs to. */
    HierarchyClassImpl attributeClass;

    /**
     * Private constructor for a hierarchy attribute. Attributes can only be created with the factory.
     *
     * @param value          The compact integer index that is used to lookup the actual hierarchy value in the creating
     *                       factory.
     * @param attributeClass The factory implementation used to create this class of hierarchy attribute.
     */
    private HierarchyAttribute(int value, HierarchyClassImpl attributeClass)
    {
        // Keep the compact representation and the class.
        this.value = value;
        this.attributeClass = attributeClass;
    }

    /**
     * Generates a factory for building hierarchy attributes of the named class.
     *
     * @param  name The name of the hierarchy attribute class to get a factory for.
     *
     * @return The hierarchy attribute factory for the named hierarchy attribute class.
     */
    public static HierarchyAttributeFactory getFactoryForClass(String name)
    {
        return HierarchyClassImpl.getInstance(name);
    }

    /**
     * Gets the {@link com.thesett.aima.state.Type} for a named class of hierarchy attributes.
     *
     * @param  name The name of the hierarchy attribute class to get the type for.
     *
     * @return The hierarchy attribute type for the named hierarchy attribute class.
     */
    public static HierarchyType getTypeForClass(String name)
    {
        return HierarchyClassImpl.getInstance(name);
    }

    /**
     * Should return a correct instance of the type class for this attribute.
     *
     * @return The attribute type of this attribute.
     */
    public Type<HierarchyAttribute> getType()
    {
        return attributeClass;
    }

    /**
     * Returns the factory that created this attribute.
     *
     * @return The factory that created this attribute.
     */
    public HierarchyAttributeFactory getFactory()
    {
        return attributeClass;
    }

    /**
     * Converts the object representation into a compact in representation.
     *
     * @return The compact in representation of this attribute.
     */
    public int getIntFromAttribute()
    {
        // Return the int representation that has alrady been created.
        return value;
    }

    /**
     * Should return an integer index for the current value of this attribute from 0 to num possible values where the
     * number of possible values is finite.
     *
     * @return An integer index for the current value of this attribute from 0 to num possible values where the number
     *         of possible values is finite.
     *
     * @throws com.thesett.aima.state.InfiniteValuesException If the set of values cannot be indexed because it is
     *                                                        infinite or cannot be ordered.
     */
    public int ordinal() throws InfiniteValuesException
    {
        // Return the int representation that has alrady been created.
        return value;
    }

    /**
     * Tests if two hierarchy attributes are equal. They are equal if they have the sequence of path labels and are of
     * the same hierarchy attribute class.
     *
     * @param  o The object to compare to.
     *
     * @return <tt>true</tt> if the comparator is a hierarchy attribute and has the same value as this one.
     */
    public boolean equals(Object o)
    {
        // Check that the comparator is also a hierarchy atribute of the same class as this one.
        return (o instanceof HierarchyAttribute) &&
            ((((HierarchyAttribute) o).value == value) &&
                ((HierarchyAttribute) o).attributeClass.attributeClassName.equals(attributeClass.attributeClassName));
    }

    /**
     * Computes a hash code based on the compact int representation, so that hash codes are identical for hierarchy
     * attributes that are equal.
     *
     * @return A hash code.
     */
    public int hashCode()
    {
        return Integer.valueOf(value).hashCode();
    }

    /**
     * Tests if another hierarchy attribute is a sub-category of this one. It is a sub-category if it has the same
     * sequence of path labels as this one as a prefix of its whole path label.
     *
     * @param  comp The hierarchy attribute to compare to.
     *
     * @return True if the comparator is of the same type and is a sub-category of this one.
     */
    public boolean isEqualOrSubCategory(HierarchyAttribute comp)
    {
        // Check that the comparator is of the same type class as this one.
        if (!comp.attributeClass.attributeClassName.equals(attributeClass.attributeClassName))
        {
            return false;
        }

        // Extract the path labels from this and the comparator.
        List<String> otherPath = comp.getPathValue();
        List<String> path = getPathValue();

        // Check that the path length of the comparator is the same as this or longer.
        if (otherPath.size() < path.size())
        {
            return false;
        }

        // Start by assuming that the paths prefixes are the same, then walk down both paths checking they are
        // the same.
        boolean subcat = true;

        for (int i = 0; i < path.size(); i++)
        {
            // Check that the labels really are equal.
            if (!otherPath.get(i).equals(path.get(i)))
            {
                subcat = false;

                break;
            }
        }

        return subcat;
    }

    /**
     * Tests if another hierarchy attribute is strict a sub-category of this one. It is a sub-category if it has the
     * same sequence of path labels as this one as a prefix of its whole path label.
     *
     * @param  comp The hierarchy attribute to compare to.
     *
     * @return True if the comparator is of the same type and is a sub-category of this one.
     */
    public boolean isSubCategory(HierarchyAttribute comp)
    {
        // Check that the comparator is of the same type class as this one.
        if (!comp.attributeClass.attributeClassName.equals(attributeClass.attributeClassName))
        {
            return false;
        }

        // Extract the path labels from this and the comparator.
        List<String> otherPath = comp.getPathValue();
        List<String> path = getPathValue();

        // Check that the path length of the comparator is the same as this plus one or longer.
        if (otherPath.size() <= path.size())
        {
            return false;
        }

        // Start by assuming that the paths prefixes are the same, then walk down both paths checking they are
        // the same.
        boolean subcat = true;

        for (int i = 0; i < path.size(); i++)
        {
            // Check that the labels really are equal.
            if (!otherPath.get(i).equals(path.get(i)))
            {
                subcat = false;

                break;
            }
        }

        return subcat;
    }

    /**
     * Gets the path value of the hierarchy attribute as an array of labels.
     *
     * @return The path value of the hierarchy attribute as an array of labels.
     */
    public List<String> getPathValue()
    {
        return attributeClass.getPathValue(value);
    }

    /**
     * Gets the path of hierarchy attribute from the root down to this hierarchy attribute value and all hierarchy
     * attribute values in between including non-allowable ones.
     *
     * @return The path of hierarchy attribute from the root down to this hierarchy attribute value and all hierarchy
     *         attribute values in between including non-allowable ones.
     */
    public List<HierarchyAttribute> getHierarchyPath()
    {
        return attributeClass.getHierarchyPath(value);
    }

    /**
     * Returns the long id of the attribute.
     *
     * @return The long id of the attribute.
     */
    public long getId()
    {
        // Find the category for this hierarchy attribute value.
        Tree<CategoryNode> category = attributeClass.lookup.get(value);

        // Extract and return the id.
        return category.getElement().id;
    }

    /**
     * Sets the integer id of the attribute. If the attribute class is finalized this will change the value of this
     * attribute to that of the matched id, or raise an exception if no matching id exists. If the attribute class is
     * unfinalized this will change the id value of this attribute within the attribute class to the new id, provided
     * that the id has not already been assigned to another attribute value. If it has been assigned to another
     * attribute value then an exception is raised.
     *
     * @param  id The new id value.
     *
     * @throws IllegalArgumentException If the type is finalized but the id does not exist. Or if the type is
     *                                  unfinalized if the id has already been assigned to another instance of the type.
     */
    public void setId(long id) throws IllegalArgumentException
    {
        // Find the category for this hierarchy attribute value.
        Tree<CategoryNode> category = attributeClass.lookup.get(value);

        // Extract the id.
        long existingId = category.getElement().id;

        // Do nothing if the new id matches the existing one.
        if (id == existingId)
        {
            return;
        }

        // The type is finalized.
        if (attributeClass.finalized)
        {
            // Raise an illegal argument exception if the id is not known.
            HierarchyAttribute newValue = attributeClass.getAttributeFromId(id);

            // Otherwise, change the value of this attribute to that of the new id.
            this.value = newValue.value;
        }

        // The type is unfinalized.
        else
        {
            // Check if another instance of the type already has the id and raise an exception if so.
            Tree<CategoryNode> existingNode = attributeClass.idMap.get(id);

            if (existingNode != null)
            {
                throw new IllegalArgumentException("The id value, " + id +
                    ", cannot be set because another instance of this type with that " + "id already exists.");
            }

            // Assign it to this instance if the type is unfinalized. Also removing the old id mapping from the id
            // map and replacing it with the new one.
            category.getElement().id = id;
            attributeClass.idMap.remove(existingId);
            attributeClass.idMap.put(id, category);
        }
    }

    /**
     * Gets the label value at the specified level of the hierarchy.
     *
     * @param  level The level to get the label at.
     *
     * @return The label value at the specified level of the hierarchy.
     */
    public String getValueAtLevel(int level)
    {
        /*log.fine("public String getValueAtLevel(int level): called");*/

        // throw new NotImplementedException();
        List<String> pathValue = getPathValue();

        /*log.fine("pathValue.size() = " + pathValue.size());*/

        // Check if the requested level exceeds the label path size, and return null for request off the end.
        if (level >= pathValue.size())
        {
            return null;
        }
        else
        {
            return getPathValue().get(level);
        }
    }

    /**
     * Gets the label value at the named level of the hierarchy.
     *
     * @param  level The level to get the label at.
     *
     * @return The label value at the specified level of the hierarchy.
     */
    public String getValueAtLevel(String level)
    {
        /*log.fine("public String getValueAtLevel(String level): called");*/
        /*log.fine("level = " + level);*/

        int index = attributeClass.levels.indexOf(level);

        /*log.fine("index = " + index);*/

        if (index == -1)
        {
            throw new IllegalArgumentException("Level name " + level +
                " is not known to this hierarchy attribute type.");
        }

        return getValueAtLevel(index);
    }

    /**
     * Gets the label value at the last level of the hierarchy.
     *
     * @return The label value at the last level of the hierarchy.
     */
    public String getLastValue()
    {
        List<String> pathValue = getPathValue();

        return pathValue.get(pathValue.size() - 1);
    }

    /**
     * Prints as a string for debugging purposes.
     *
     * @return A string for debugging purposes.
     */
    public String toString()
    {
        String result = "value: " + value + ", " + "id: " + getId() + ", [ ";

        String[] pathValue = getPathValue().toArray(new String[0]);

        for (int i = 0; i < pathValue.length; i++)
        {
            result += pathValue[i] + ((i == (pathValue.length - 1)) ? "" : ", ");
        }

        return result + " ]";
    }

    /**
     * Serialized a hierarchy attribute.
     *
     * @param  out The object output stream.
     *
     * @throws IOException If an IO exception occurs.
     */
    private void writeObject(ObjectOutputStream out) throws IOException
    {
        // Print out some information about the serialized object.
        /*log.fine("Serialized hierarchy attribute = " + this);*/
        /*log.fine("Serialized hierarchy attribute class = " + attributeClass);*/

        /*log.fine("Serialized attribute classes in static class map are: ");*/

        for (HierarchyClassImpl attributeClass : attributeClasses.values())
        {
            /*log.fine(attributeClass.toString());*/
        }

        // Perform default serialization.
        // out.defaultWriteObject();

        // Serialized the attribute by value, that is, its full path and the name of its attribute class.
        List<String> pathValue = getPathValue();
        String[] pathArrayValue = pathValue.toArray(new String[pathValue.size()]);

        out.writeObject(pathArrayValue);
        out.writeObject(attributeClass.getName());
    }

    /**
     * Deserializes a hierarchy attribute.
     *
     * @param  in The object input stream.
     *
     * @throws IOException            If any IO exception occurs.
     * @throws ClassNotFoundException If any classes to deserialize cannot be found.
     */
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
    {
        // Perform default de-serialization.
        // in.defaultReadObject();

        // Deserialize the attribute by value, from its attribute class and full path.
        String[] pathArrayValue = (String[]) in.readObject();
        String attributeClassName = (String) in.readObject();

        // Re-create the attribute from its value representation.
        HierarchyAttribute attr = getFactoryForClass(attributeClassName).createHierarchyAttribute(pathArrayValue);

        // Copy the fields from the freshly constructed attribute into this one.
        value = attr.value;
        attributeClass = attr.attributeClass;

        // Print out some information about the deserialized object.
        /*log.fine("Deserialized hierarchy attribute = " + this);*/
        /*log.fine("Deserialized hierarchy attribute class = " + attributeClass);*/

        /*log.fine("Deserialized attribute classes in static class map are: ");*/

        for (HierarchyClassImpl attributeClass : attributeClasses.values())
        {
            /*log.fine(attributeClass.toString());*/
        }
    }

    /**
     * Defines the class of hierarchy attributes. Class = Type + Factory.
     */
    public static interface HierarchyClass extends HierarchyType, HierarchyAttributeFactory
    {
    }

    /**
     * Provides the implementation of the hierarchy attribute class.
     */
    private static class HierarchyClassImpl extends BaseType<HierarchyAttribute> implements HierarchyClass, Serializable
    {
        /** The number of possible values this attribute can take on. Defaults to infinity. */
        int numValues = -1;

        /** The hierarchies level names. */
        List<String> levels;

        /**
         * Holds a map from int representations to category nodes. This is used while the class is unfinalized, once it
         * is finalized the map if transferred into the lookup array.
         */
        List<Tree<CategoryNode>> lookup = new ArrayList<Tree<CategoryNode>>();

        /**
         * Holds a map from referencable ids to category nodes. This is used to resolve ids into instances of the
         * attribute type. It is a map rather than an array because the ids do not have to be sequentially assigned from
         * 0 but could be a scattered range of values.
         */
        Map<Long, Tree<CategoryNode>> idMap = new HashMap<Long, Tree<CategoryNode>>();

        /** Holds a tree representation of all categories in the class. */
        SimpleTree<CategoryNode> categories; // new SimpleTree<CategoryNode>();

        /** Holds the next available integer for the compact representation enumeration. */
        int nextValue;

        /** Keeps track of the maximum number of levels in the type. */
        int maxLevels;

        /** Used to indicate whether the class has been finalized yet. */
        boolean finalized;

        /** Used to hold the name of the attribute class that this is a factory for. */
        String attributeClassName;

        /**
         * Builds a new factory for a given class.
         *
         * @param name The name that identifies this class of hierarchy attributes.
         */
        private HierarchyClassImpl(String name)
        {
            // Keep a reference to the attribute class and name.
            this.attributeClassName = name;
        }

        /**
         * Gets an instance of this factory implementation for specified class of hierarchy attribute. If the attribute
         * class is a new one, then a new factory instance is created, otherwise an existing factory for the class is
         * used.
         *
         * @param  name The name that identifies this class of hierarchy attributes.
         *
         * @return A factory implementation for the named hierarchy attribute class.
         */
        public static HierarchyClassImpl getInstance(String name)
        {
            // Try to get the attribute class from the map of those that have already been created.
            HierarchyClassImpl attributeClass = attributeClasses.get(name);

            // Check if this is a new class and create it if so.
            if (attributeClass == null)
            {
                attributeClass = new HierarchyClassImpl(name);
                attributeClasses.put(name, attributeClass);
            }

            return attributeClass;
        }

        /**
         * Gets a new default instance of the type. The types value will be set to its default uninitialized value.
         *
         * @return A new default instance of the type. Always <tt>false</tt>.
         */
        public HierarchyAttribute getDefaultInstance()
        {
            throw new NotImplementedException();
        }

        /**
         * Gets the hierarchy attribute type for this factory.
         *
         * @return The hierarchy attribute type for this factory.
         */
        public HierarchyType getType()
        {
            return this;
        }

        /**
         * Gets the underlying Java class that implements the type.
         *
         * @return The underlying Java class that implements the type.
         */
        public Class<HierarchyAttribute> getBaseClass()
        {
            return HierarchyAttribute.class;
        }

        /** {@inheritDoc} */
        public String getBaseClassName()
        {
            return getBaseClass().getName();
        }

        /**
         * Sets up the level names of the hierarchy.
         *
         * @param levels The level names.
         */
        public void setLevelNames(String[] levels)
        {
            for (String level : levels)
            {
                // Check that none of the level names are null or empty
                if (Validation.isEmpty(level))
                {
                    throw new IllegalArgumentException("Null or empty level names are not allowed.");
                }
            }

            // Keep the level names.
            this.levels = new ArrayList(levels.length);

            for (String level : levels)
            {
                this.levels.add(level);
            }
        }

        /**
         * Gets the level names.
         *
         * @return The level names.
         */
        public String[] getLevelNames()
        {
            return levels.toArray(new String[levels.size()]);
        }

        /**
         * Creates a hierarchy attribute of the class that this is a factory for. If the attribute class has been
         * finalized and the requested value is not in the class then this should raise an exception.
         *
         * @param  value The value that the hierarchy attribute should have.
         *
         * @return A new hierarchy attribute with the specified value.
         *
         * @throws IllegalArgumentException If the type has been finalized but the value to create an attribute for if
         *                                  not a member of the type.
         */
        public HierarchyAttribute createHierarchyAttribute(String[] value) throws IllegalArgumentException
        {
            // Run a search and try to find a category matching the specified sequence of labels.
            SearchState searchState = findMatchingCategory(value);

            // Check for error conditions.
            // Check if the attribute class has been finalized, a match has been found, but it is not allowable.
            if (finalized && searchState.foundMatch && !searchState.currentCategory.getElement().allowable)
            {
                throw new IllegalArgumentException("A hierarchy attribute cannot be created for the specified " +
                    "value as it is not an allowable category.");
            }

            // Check if there has not been a match but the type is finalized.
            if (finalized && !searchState.foundMatch)
            {
                // The value is not already in the attribute class and it is finalized so an attribute
                // cannot be created.
                throw new IllegalArgumentException("A hierarchy element cannot be created because the type, " +
                    attributeClassName + ", has been finalized and does not contain " + "the requested value.");
            }

            // Check if there has been a match and the type is not finalized then make sure that the matched category is
            // made allowable as it may not already be.
            if (!finalized && searchState.foundMatch)
            {
                // Make the matched value allowable if it is not already.
                searchState.currentCategory.getElement().allowable = true;
            }

            // The value is not already in the attribute class but it is not finalized yet, so add the remaining
            // labels to the category tree.
            else if (!finalized && !searchState.foundMatch)
            {
                SearchState rootState = new SearchState();
                rootState.currentCategory = categories;
                rootState.foundMatch = false;
                rootState.lastPosition = 0;

                searchState = insertNewCategory(searchState, value);
            }

            // Create a new hierarcy attribute from the compact int representation of the terminal position in the
            // category tree of the search or search/insert procedures.
            return new HierarchyAttribute(searchState.currentCategory.getElement().value, this);
        }

        /**
         * Creates a hierarchy attribute of the class that this is a factory for. This method differs from the
         * {@link #createHierarchyAttribute} method in that it can create non-allowable hierarchy attributes. This
         * method will only create hierarchy attributes for values that already exist in the hierarchy class, whether or
         * not it has been finalized. Its purpose is to create any path within a hierarchy attribute for equality or
         * sub-type comparision with other hierarchy attributes.
         *
         * @param  value The value that the hierarchy attribute should have.
         *
         * @return A new hierarchy attribute with the specified value.
         *
         * @throws IllegalArgumentException If the value is not already a memeber of the type.
         */
        public HierarchyAttribute createHierarchyAttributeForComparison(String[] value) throws IllegalArgumentException
        {
            // Run a search and try to find a category matching the specified sequence of labels.
            SearchState searchState = findMatchingCategory(value);

            // Check for error conditions.
            // Check if no match was found.
            if (!searchState.foundMatch)
            {
                throw new IllegalArgumentException("A hierarchy attribute for comparison can only be created for " +
                    "value that already exist in the type.");
            }

            // Create a new hierarcy attribute from the compact int representation of the terminal position in the
            // category tree of the search or search/insert procedures.
            return new HierarchyAttribute(searchState.currentCategory.getElement().value, this);
        }

        /**
         * Returns the list of labels from the root of the category tree to the specified category.
         *
         * @param  currentValue The int value of the category to get the list of labels for.
         *
         * @return The list of labels from the root of the category tree to the specified category.
         */
        public List<String> getPathValue(int currentValue)
        {
            /*log.fine("public List<String> getPathValue(int currentValue): called");*/
            /*log.fine("currentValue = " + currentValue);*/

            // Get the point in the tree of categories that corresponds to the specified value.
            Tree<CategoryNode> category = lookup.get(currentValue);
            /*log.fine("Matching category = " + category);*/

            // Push all the labels onto a stack, up to but not including the invisible root of the tree.
            Stack<String> stack = new Stack<String>();

            do
            {
                stack.push(category.getElement().label);

                /*log.fine("Pushed " + category.getElement().label + " onto the stack.");*/

                category = category.getParent();
            }
            while ((category != null) && (category != categories));

            // Reverse the stack by popping everything into a list.
            List<String> result = new ArrayList(stack.size());

            int stackSize = stack.size();

            for (int i = 0; i < stackSize; i++)
            {
                result.add(stack.pop());
            }

            /*log.fine("Complete path value = " + result);*/

            // Return the stack as a list.
            return result;
        }

        /**
         * Gets the path of hierarchy attribute from the root down to this hierarchy attribute value and all hierarchy
         * attribute values in between including non-allowable ones.
         *
         * @param  currentValue The int value of the category to get the list of values for.
         *
         * @return The path of hierarchy attribute from the root down to this hierarchy attribute value and all
         *         hierarchy attribute values in between including non-allowable ones.
         */
        public List<HierarchyAttribute> getHierarchyPath(int currentValue)
        {
            // Get the point in the tree of categories that corresponds to the specified value.
            Tree<CategoryNode> category = lookup.get(currentValue);

            // Push all the labels onto a stack, up to but not including the invisible root of the tree.
            Stack<HierarchyAttribute> stack = new Stack<HierarchyAttribute>();

            do
            {
                stack.push(new HierarchyAttribute(category.getElement().value, this));
                category = category.getParent();
            }
            while ((category != null) && (category != categories));

            // Reverse the stack by popping everything into a list.
            List<HierarchyAttribute> result = new ArrayList(stack.size());

            int stackSize = stack.size();

            for (int i = 0; i < stackSize; i++)
            {
                result.add(stack.pop());
            }

            // Return the stack as a list.
            return result;
        }

        /**
         * Reports the maximum number of levels that this hierarchy attribute type contains.
         *
         * @return The maximum number of levels that this hierarchy attribute type contains.
         */
        public int getMaxLevels()
        {
            return maxLevels;
        }

        /**
         * Converts a compact int representation of a hierarchy attribute into its object representation. If the int is
         * not valid then this will raise an exception.
         *
         * @param  i The compact int index that a hierarchy attribute should be created from.
         *
         * @return A hierarchy attribute looked up by its in index.
         */
        public HierarchyAttribute getAttributeFromInt(int i)
        {
            // Check that the value exists.
            if (i >= lookup.size())
            {
                throw new IllegalArgumentException("The specified hierarchy element cannot be generated from the " +
                    "compact representation, " + i + ", because that does not represent an existing value.");
            }

            // Check that the value is allowable.
            Tree<CategoryNode> matchedCategory = lookup.get(i);

            if (!matchedCategory.getElement().allowable)
            {
                throw new IllegalArgumentException("The specified hierarchy element cannot be generated from the " +
                    "compact representation, " + i + ", because that does not represent an allowable value.");
            }

            return new HierarchyAttribute(i, this);
        }

        /**
         * Converts an id representation of a hierarchy attribute into its object representation. If the id is not valid
         * then this will raise an exception.
         *
         * @param  id The id of the hierarchy attribute value to fetch.
         *
         * @return A hierarchy attribute looked up by its id.
         */
        public HierarchyAttribute getAttributeFromId(long id)
        {
            // The id is only valid if it is in the id map and is allowable.
            Tree<CategoryNode> matchedCategory = idMap.get(id);

            // Check that the value exists.
            if (matchedCategory == null)
            {
                throw new IllegalArgumentException("The specified hierarchy element cannot be generated from the id, " +
                    id + ", because that does not represent an existing value.");
            }

            // Check that the value is allowable.
            if (!matchedCategory.getElement().allowable)
            {
                throw new IllegalArgumentException("The specified hierarchy element cannot be generated from the id, " +
                    id + ", because that does not represent an allowable value.");
            }

            return new HierarchyAttribute(matchedCategory.getElement().value, this);
        }

        /** Finalizes an attribute class. This prevents any new attribute value from being added to the class. */
        public void finalizeAttribute()
        {
            /*log.fine("public void finalizeAttribute(): called");*/
            /*log.fine("categories = " + categories);*/

            // Check if the attribute class has already been finalized and do nothing if so.
            if (finalized)
            {
                // Its already been finalized. Do nothing.
                return;
            }

            // Count how many allowable values there are and set this as the possible value count.
            int allowableValues = 0;

            // Ensure that some categories actually exist to be counted.
            if (categories != null)
            {
                // Used to ensure that the first category, corresponding to the invisible root node is skipped.
                boolean firstCategory = true;

                for (Iterator<SimpleTree<CategoryNode>> i = categories.iterator(Tree.IterationOrder.PreOrder);
                        i.hasNext();)
                {
                    // Skip the invisible root node.
                    if (firstCategory)
                    {
                        firstCategory = false;
                        i.next();

                        continue;
                    }

                    Tree<CategoryNode> nextCategory = i.next();
                    /*log.fine("nextCategory.getElement() = " + nextCategory.getElement());*/

                    // Increment the allowable values only if it is allowable.
                    allowableValues = (nextCategory.getElement().allowable) ? (allowableValues + 1) : allowableValues;
                }
            }

            numValues = allowableValues;

            // Set the finalized flag.
            finalized = true;
        }

        /** Drops an attribute class. The attribute class is explicitly deleted. */
        public void dropAttributeClass()
        {
            attributeClasses.remove(attributeClassName);
        }

        /**
         * Returns a name that uniquely identifies this hierarchy attribute type.
         *
         * @return The name of the hierarchy attribute type.
         */
        public String getName()
        {
            return attributeClassName;
        }

        /**
         * Determines how many different values an attribute of this type can take on.
         *
         * @return The number of possible values that an instance of this attribute can take on.
         */
        public int getNumPossibleValues()
        {
            // Fetch the number of possible values that this attribute class can take.
            return numValues;
        }

        /**
         * Returns all the different values that an OrdinalAttribute of this type can take on.
         *
         * @return A set of attributes defining the possible value set for this attribute.
         */
        public Set<HierarchyAttribute> getAllPossibleValuesSet()
        {
            return getAllPossibleValuesSet(true);
        }

        /** {@inheritDoc} */
        public Set<HierarchyAttribute> getAllPossibleValuesSet(boolean failOnNonFinalized)
        {
            // Check if the attribute class is not yet finalized so an infinte values exception must be thrown.
            if (!finalized && failOnNonFinalized)
            {
                throw new InfiniteValuesException("The string attribute class is not finalized yet, " +
                    "so can have infinite values.", null);
            }

            // The attribute class is finalized so return the array of possible values as a set.
            Set<HierarchyAttribute> result = new HashSet<HierarchyAttribute>();

            // Loop over the whole lookup array, extracting just those values that are allowable.
            for (Tree<CategoryNode> nextCategory : lookup)
            {
                CategoryNode nextNode = nextCategory.getElement();

                if (nextNode.allowable)
                {
                    result.add(new HierarchyAttribute(nextNode.value, this));
                }
            }

            return result;
        }

        /**
         * Returns all the different values that an OrdinalAttribute of this type can take on as an iterator over these
         * values. The hierarchy forms a tree the leaves of which are values that it can take on. The iterator returns
         * the leaves 'in-order'.
         *
         * @return An iterator over the set of attributes defining the possible value set for this attribute.
         */
        public Iterator<HierarchyAttribute> getAllPossibleValuesIterator()
        {
            return getAllPossibleValuesSet(true).iterator();
        }

        /** {@inheritDoc} */
        public Iterator<HierarchyAttribute> getAllPossibleValuesIterator(boolean failOnNonFinalized)
        {
            return getAllPossibleValuesSet(failOnNonFinalized).iterator();
        }

        /**
         * Generates the set of all the values (not just the allowable ones) in the hierarchy type, at the named level.
         *
         * @param  level The name of the level to list.
         *
         * @return The set of all the values (not just the allowable ones) in the hierarchy type, at the named level.
         */
        public Set<HierarchyAttribute> getValuesAtLevelSet(String level)
        {
            /*log.fine("public Set<HierarchyAttribute> getValuesAtLevelSet(String level = " + level + "): called");*/

            // Get the maximum depth to list down to.
            int depth = levels.indexOf(level);

            /*log.fine("depth = " + depth);*/

            // Check that the requested level was actually found.
            if (depth == -1)
            {
                throw new IllegalArgumentException("Level, " + level + ", is not recognized.");
            }

            // Search down to the requested depth, including both allowable and non-allowable elements in the result.
            Set<HierarchyAttribute> results = new HashSet<HierarchyAttribute>();
            findAllMatchingAttributes(depth, new TrueUnaryPredicate(), results);

            return results;
        }

        /**
         * Generates the set of all the values (not just the allowable ones) in the hierarchy type that are
         * sub-hierarchies of the specified value, at the named level.
         *
         * @param  parent The parent hierarchy to list the siblings of.
         * @param  level  The name of the level to list.
         *
         * @return The set of all the values (not just the allowable ones) in the hierarchy type, at the named level.
         */
        public Set<HierarchyAttribute> getSubHierarchyValuesSet(HierarchyAttribute parent, String level)
        {
            /*log.fine(
                "public Set<HierarchyAttribute> getSubHierarchyValuesSet(HierarchyAttribute parent, String level): called");*/
            /*log.fine("parent = " + parent);*/
            /*log.fine("level = " + level);*/

            // Get the maximum depth to list down to.
            int depth = levels.indexOf(level);

            /*log.fine("depth = " + depth);*/

            // Check that the requested level was actually found.
            if (depth == -1)
            {
                throw new IllegalArgumentException("Level, " + level + ", is not recognized.");
            }

            // Create a strict sub-category selection predicate.
            final HierarchyAttribute parentRef = parent;
            final HierarchyClassImpl thisRef = this;

            UnaryPredicate<TreeSearchState<CategoryNode>> strictSubCategoryPredicate =
                new UnaryPredicate<TreeSearchState<CategoryNode>>()
                {
                    public boolean evaluate(TreeSearchState<CategoryNode> c)
                    {
                        return parentRef.isSubCategory(new HierarchyAttribute(c.getElement().value, thisRef));
                    }
                };

            // Search down to the requested depth, including both allowable and non-allowable elements in the result.
            // Create a bounded depth first search method.
            Set<HierarchyAttribute> results = new HashSet<HierarchyAttribute>();
            findAllMatchingAttributes(depth, strictSubCategoryPredicate, results);

            return results;
        }

        /**
         * Generates an iterator over all the values (not just the allowable ones) in the hierarchy type, at the named
         * level.
         *
         * @param  level The name of the level to list.
         *
         * @return An iterator over all the values (not just the allowable ones) in the hierarchy type, at the named
         *         level.
         */
        public Iterator<HierarchyAttribute> getValuesAtLevelIterator(String level)
        {
            return getValuesAtLevelSet(level).iterator();
        }

        /**
         * Generates an iterator over all the values (not just the allowable ones) in the hierarchy type that are
         * sub-hierarchies of the specified value, at the named level.
         *
         * @param  parent The parent hierarchy to list the siblings of.
         * @param  level  The name of the level to list.
         *
         * @return An iterator over all the values (not just the allowable ones) in the hierarchy type, at the named
         *         level.
         */
        public Iterator<HierarchyAttribute> getSubHierarchyValuesIterator(HierarchyAttribute parent, String level)
        {
            return getSubHierarchyValuesSet(parent, level).iterator();
        }

        /**
         * Creates a string representation of the hierarchy attribute class for debugging purposes.
         *
         * @return A string representation of the hierarchy attribute class for debugging purposes.
         */
        public String toString()
        {
            // Build a string of all the allowable values.
            String allowableValues = "";

            // Loop over the whole lookup array, extracting just those values that are allowable.
            for (int i = 0; i < lookup.size(); i++)
            {
                Tree<CategoryNode> nextCategory = lookup.get(i);
                CategoryNode nextNode = nextCategory.getElement();

                if (nextNode.allowable)
                {
                    allowableValues += "[" + nextNode.toString() + "]" + ((i == (lookup.size() - 1)) ? "" : ", ");
                }
            }

            return "attributeClassName: " + attributeClassName + ", all allowable values: " + allowableValues;
        }

        /** {@inheritDoc} */
        public void acceptVisitor(TypeVisitor visitor)
        {
            if (visitor instanceof HierarchyTypeVisitor)
            {
                ((HierarchyTypeVisitor) visitor).visit(this);
            }
            else
            {
                super.acceptVisitor(visitor);
            }
        }

        /**
         * Finds all hierarchy attributes matching the specified predicate, down to the specified depth.
         *
         * @param depth     The maximum depth to search to.
         * @param predicate The predicate to match.
         * @param results   A set to add the matching results to.
         */
        private void findAllMatchingAttributes(int depth, UnaryPredicate<TreeSearchState<CategoryNode>> predicate,
            Collection<HierarchyAttribute> results)
        {
            QueueBasedSearchMethod<Tree<CategoryNode>, TreeSearchState<CategoryNode>> search =
                new DepthBoundedSearch<Tree<CategoryNode>, TreeSearchState<CategoryNode>>(depth);
            search.reset();

            // Add only children of the invisible root node as start states to the search.
            for (Tree<CategoryNode> initialChild : categories.getChildren())
            {
                search.addStartState(new TreeSearchState<CategoryNode>(initialChild));
            }

            search.setGoalPredicate(predicate);

            SearchNode goal;

            try
            {
                do
                {
                    goal = search.findGoalPath();

                    // Check if a goal state was found.
                    if (goal != null)
                    {
                        // Get the goal state.
                        TreeSearchState<CategoryNode> goalState = (TreeSearchState<CategoryNode>) goal.getState();

                        // Extract the data element for the goal state.
                        CategoryNode goalNode = goalState.getElement();

                        // Create a hierarchy attribute for the goal.
                        HierarchyAttribute found = new HierarchyAttribute(goalNode.value, this);
                        /*log.fine("Found attribute: " + found);*/

                        results.add(found);
                    }
                }
                while (goal != null);
            }

            // There should be search failure exceptions when the maximum depth is reached. Can safely ignore as the
            // search termination point.
            catch (SearchNotExhaustiveException e)
            {
                /*log.log(java.util.logging.Level.FINE, "Got a search failure exception: ", e);*/
            }
        }

        /**
         * Inserts a new sequence of labels into the category tree starting from the specified search state.
         *
         * @param  searchState The tree search state to insert at.
         * @param  value       The labels to insert.
         *
         * @return The search state at the end of the insertion.
         */
        private SearchState insertNewCategory(SearchState searchState, String[] value)
        {
            /*log.fine("private SearchState insertNewCategory(SearchState searchState, String[] value): called");*/
            /*log.fine("searchState = " + searchState);*/

            // Start from where the matching search left off.
            for (int i = searchState.lastPosition; i < value.length; i++)
            {
                /*log.fine("i = " + i);*/

                // Add a new label to the category tree, assigning it the next available int value.
                CategoryNode newNode = new CategoryNode();
                newNode.label = value[i];
                newNode.value = nextValue++;
                newNode.id = (long) newNode.value;

                // Check if this is the last label and make it allowable if so.
                if (i == (value.length - 1))
                {
                    /*log.fine("Last label, making node allowable.");*/
                    newNode.allowable = true;
                }

                /*log.fine("Created new node to insert: " + newNode);*/

                // Hook it into the tree.
                Tree<CategoryNode> newChild = new SimpleTree<CategoryNode>();
                newChild.setElement(newNode);

                // If this is the first node in the tree then create the initial tree.
                if (categories == null)
                {
                    categories = new SimpleTree<CategoryNode>();
                    categories.addChild(newChild);
                    //categories = (SimpleTree<CategoryNode>) categories.getChild(0);

                    //categories = newChild;
                    /*log.fine("First node in the tree, set as categories root.");*/
                }
                else
                {
                    searchState.currentCategory.addChild(newChild);
                    /*log.fine("Added as child of current search state.");*/
                }

                // Add it to the map from integer values to categories.
                lookup.add(newNode.value, newChild);

                // Add it to the map from ids to categories.
                idMap.put(newNode.id, newChild);

                // Update maxLevels if a higher greater level has been reached than before.
                if ((searchState.lastPosition + 1) > maxLevels)
                {
                    maxLevels = searchState.lastPosition + 1;
                    /*log.fine("Updated maxLevels to " + maxLevels);*/
                }

                // Advance the current position in the category to the child node just created.
                searchState.currentCategory = newChild;
                searchState.lastPosition++;

                /*log.fine("New search state = " + searchState);*/
                /*log.fine("At end of loop, categories = " + categories);*/
            }

            /*log.fine("At end of insert, categories = " + categories);*/

            return searchState;
        }

        /**
         * Searches the tree of categories for a {@link SearchState} that exactly matches the specified list (path) of
         * labels, or the search state corresponding to the longest path of labels that exists already in the tree of
         * categories, that matches some of the values in the specified list.
         *
         * @param  value The path of labels to search for.
         *
         * @return The search state where a match was found or where the search terminated because no match can be
         *         found.
         */
        private SearchState findMatchingCategory(String[] value)
        {
            /*log.fine("private SearchState findMatchingCategory(String[] value): called");*/
            /*log.fine("categories = " + categories);*/

            // Create a search state to encapsulate the state at the end of the search;
            SearchState searchState = new SearchState();

            // Loop over the new value to see if it can be found in the existing category tree.
            // Start at the root category.
            searchState.currentCategory = categories;

            // Used to indicate that a match has been found.
            searchState.foundMatch = false;

            // Holds the last position in the value to be set where the search for a match stopped.
            searchState.lastPosition = 0;

            // Check that some categories do exist, otherwise return the unstarted search state.
            if (categories == null)
            {
                /*log.fine("At end of find, searchState = " + searchState);*/

                return searchState;
            }

            // Make the children of the starting node the first set of child nodes to be examined.
            Collection<Tree<CategoryNode>> initialChildCollection = new ArrayList<Tree<CategoryNode>>();

            for (Tree<CategoryNode> initialChild : categories.getChildren())
            {
                initialChildCollection.add(initialChild);
                /*log.fine("Set " + initialChild + " as an initial child node to examine.");*/
            }

            Iterator<Tree<CategoryNode>> childIterator = initialChildCollection.iterator();

            for (int i = 0; i < value.length; i++)
            {
                // Get the next label in the value.
                String nextLabel = value[i];
                /*log.fine("nextLabel = " + nextLabel);*/

                // Label at current position examined but not yet matched, so record the last position as this label.
                searchState.lastPosition = i;

                // Examine the next set of child nodes for a matching label.
                boolean foundLabelMatch = false;

                while (childIterator.hasNext())
                {
                    Tree<CategoryNode> nextSubCategory = childIterator.next();

                    // Check if the current label matches the sub-category.
                    if (nextSubCategory.getElement().label.equals(nextLabel))
                    {
                        /*log.fine("Found match for nextLabel.");*/

                        // Move the current category down to the matched sub-category.
                        searchState.currentCategory = nextSubCategory;

                        // Record that a match has been found.
                        foundLabelMatch = true;

                        // Label at current position examined and matched, so record the last position as the next
                        // label.
                        searchState.lastPosition = i + 1;

                        // Check if this was the last label to match and set the success flag if so.
                        if (i == (value.length - 1))
                        {
                            /*log.fine("Completed match for whole path.");*/
                            searchState.foundMatch = true;
                        }

                        // Stop scanning the sub-categories.
                        break;
                    }
                }

                // Check if the whole value has been matched and terminate the search if so.
                if (searchState.foundMatch)
                {
                    break;
                }

                // Check if a label match was found and move on to the next label if so, but only if there are
                // child nodes to examine.
                else if (foundLabelMatch)
                {
                    // Set up the children, if any, of the matched child node for the next round of matching.
                    if (!searchState.currentCategory.isLeaf())
                    {
                        childIterator = searchState.currentCategory.getAsNode().getChildIterator();
                    }

                    // There are no more child nodes to match, so stop the search here.
                    else
                    {
                        break;
                    }

                    // Continue searching the child nodes for a match.
                    // continue;
                }

                // A label match was not found so the match cannot be completed. The new value may still be inserted
                // into the category tree provided the type has not been finalized. Terminate the search for a match.
                else
                {
                    break;
                }
            }

            /*log.fine("At end of find, searchState = " + searchState);*/

            return searchState;
        }
    }

    /**
     * A CategoryNode captures all the relevant information about a node within a tree of categories; its label, its
     * enumerated compact int representation, whether or not it is an allowable value.
     */
    private static class CategoryNode implements Serializable
    {
        /** The label of the node. */
        public String label;

        /** The compact int value of the node. */
        public int value;

        /** Flag to indicate whether it is an allowable value. */
        public boolean allowable;

        /** The referencable id of the value. */
        public long id;

        /**
         * Outputs all values as a string for debugging.
         *
         * @return All values as a string for debugging.
         */
        public String toString()
        {
            return "label = " + label + ": value = " + value + ", allowable = " + allowable + ", id = " + id;
        }
    }

    /**
     * Used to encapsulate the state of a search over the tree of categories when a search needs to be stopped because
     * no match has been found, but then restarted to insert a new value where this is permitted.
     */
    private static class SearchState
    {
        /** The point in the tree of categories last examined. */
        Tree<CategoryNode> currentCategory;

        /** Whether or not a succesfull match was found. */
        boolean foundMatch;

        /** The last position seen in the array of labels being scanned for a match. */
        int lastPosition;

        /**
         * Prints search state as a string for debugging purposes.
         *
         * @return search state as a string for debugging purposes.
         */
        public String toString()
        {
            return "currentCategory = " + currentCategory + ", foundMatch = " + foundMatch + ", lastPosition = " +
                lastPosition;
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed 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 com.thesett.aima.search.examples.eightpuzzle;

import com.thesett.aima.search.util.informed.Heuristic;
import com.thesett.aima.search.util.informed.HeuristicSearchNode;

/**
 * Implements the manhattan distance heuristic for the eight puzzle. If there is a parent heuristic search node, which
 * there will be for all states other than the start state, then it calculates the manhattan heuristic incrementally
 * from it by only altering the parent heuristic for the one tile that was moved.
 *
 *

CRC Card

 *
Responsibilities  Collaborations
 *
 Calculate the manhattan distance of all tiles from the goal state.
 *      {@link HeuristicSearchNode}, {@link EightPuzzleState}
 *

 *
 * @author Rupert Smith
 */
public class ManhattanHeuristic implements Heuristic<String, EightPuzzleState>
{
    /**
     * Returns heuristic evaluation of an eight puzzle board position as the manhattan distance of all tiles from their
     * correct positions.
     *
     * @param  state      The EightPuzzleState to calculate the heuristic for.
     * @param  searchNode The heuristic search node for the state in some heuristic search.
     *
     * @return The heuristic estimation of the cost to the goal from this board position.
     */
    public float computeH(EightPuzzleState state, HeuristicSearchNode searchNode)
    {
        // Get the parent heuristic search node.
        HeuristicSearchNode parentNode = (HeuristicSearchNode) searchNode.getParent();

        // Check if there is no parent, in which case this is the start state so the complete heuristic needs
        // to be calculated.
        if (parentNode == null)
        {
            // Used to hold the running total.
            int h = 0;

            // Loop over the whole board.
            for (int j = 0; j < 3; j++)
            {
                for (int i = 0; i < 3; i++)
                {
                    char nextTile = state.getTileAt(i, j);

                    // Look up the board position of the tile in the solution.
                    int goalX = state.getGoalXForTile(nextTile);
                    int goalY = state.getGoalYForTile(nextTile);

                    // Compute the manhattan distance and add it to the total.
                    int diffX = goalX - i;

                    diffX = (diffX < 0) ? -diffX : diffX;

                    int diffY = goalY - j;

                    diffY = (diffY < 0) ? -diffY : diffY;

                    h += diffX + diffY;
                }
            }

            // Convert the result to a float and return it
            return (float) h;
        }

        // There is a parent node so calculate the heuristic incrementally from it.
        else
        {
            // Get the parent board state.
            EightPuzzleState parentState = (EightPuzzleState) parentNode.getState();

            // Get the parent heurstic value.
            float h = parentNode.getH();

            // Get the move that was played.
            char playedMove = ((String) searchNode.getAppliedOp().getOp()).charAt(0);

            // Get the position of the empty tile on the parent board.
            int emptyX = parentState.getEmptyX();
            int emptyY = parentState.getEmptyY();

            // Work out which tile has been moved, this is the tile that now sits where the empty tile was.
            char movedTile = state.getTileAt(emptyX, emptyY);

            // The tile has either moved one step closer to its goal location or one step further away, decide which it
            // is. Calculate the X or Y position that the tile moved from.
            int oldX = 0;
            int oldY = 0;

            switch (playedMove)
            {
            case 'L':
            {
                oldX = emptyX - 1;
                break;
            }

            case 'R':
            {
                oldX = emptyX + 1;
                break;
            }

            case 'U':
            {
                oldY = emptyY - 1;
                break;
            }

            case 'D':
            {
                oldY = emptyY + 1;
                break;
            }

            default:
            {
                throw new IllegalStateException("Unkown operator: " + playedMove + ".");
            }
            }

            // Calculate the change in heuristic.
            int change = 0;

            switch (playedMove)
            {
            // Catch the case where a horizontal move was made.
            case 'L':
            case 'R':
            {
                // Get the X position of the tile in the goal state and current state
                int goalX = state.getGoalXForTile(movedTile);
                int newX = emptyX;

                // Calculate the change in the heuristic
                int oldDiffX = oldX - goalX;

                oldDiffX = (oldDiffX < 0) ? -oldDiffX : oldDiffX;

                int newDiffX = newX - goalX;

                newDiffX = (newDiffX < 0) ? -newDiffX : newDiffX;

                change = newDiffX - oldDiffX;
                break;
            }

            // Catch the case where a vertical move was made.
            case 'U':
            case 'D':
            {
                // Get the Y position of the tile in the goal state and current state
                int goalY = state.getGoalYForTile(movedTile);
                int newY = emptyY;

                // Calculate the change in the heuristic
                int oldDiffY = oldY - goalY;

                oldDiffY = (oldDiffY < 0) ? -oldDiffY : oldDiffY;

                int newDiffY = newY - goalY;

                newDiffY = (newDiffY < 0) ? -newDiffY : newDiffY;

                change = newDiffY - oldDiffY;

                break;
            }

            default:
            {
                throw new IllegalStateException("Unkown operator: " + playedMove + ".");
            }
            }

            // Return the parent heuristic plus or minus one.
            return (change > 0) ? (h + 1.0f) : (h - 1.0f);
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed 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 com.thesett.common.util.maps;

import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import com.thesett.common.error.NotImplementedException;
import com.thesett.common.util.Queue;

/**
 * An key queued map is a data structure in which the elements may be referenced by a key in the same way as a
 * {@link java.util.Map} as well as by an ordering of the keys provided by an arbitrary queue implementation. Different
 * queue implementations may be provided in order to create different orderings and behaviours of the map.
 *
 *

CRC Card

 *
Responsibilities
 *

 *
 * @author Rupert Smith
 */
public abstract class KeyQueuedMap<K, V> implements Map<K, V>
{
    /** The HashMap that holds the key to index mapping. */
    private final Map<K, V> dataMap;

    /** The queue that holds the keys in order. */
    private final Queue<K> keys;

    /** Creates a new empty hash array. */
    public KeyQueuedMap()
    {
        this.dataMap = new HashMap<K, V>();
        this.keys = createQueue();
    }

    /**
     * Creates a new empty hash array with the specified starting size.
     *
     * @param size The initial size of the hash array to create.
     */
    public KeyQueuedMap(int size)
    {
        this.dataMap = new HashMap<K, V>(size);
        this.keys = createQueue();
    }

    /**
     * This abstract method should be overriden to return an empty queue of keys. Different implementations of queued
     * key maps can control the ordering and behaviour of the data structure by providing different queue
     * implementations with different properties.
     *
     * @return An empty queue of keys.
     */
    public abstract Queue<K> createQueue();

    /** Clears the whole data structure. */
    public void clear()
    {
        dataMap.clear();
        keys.clear();
    }

    /**
     * Returns true if this map contains a mapping for the specified key.
     *
     * @param  key The key to check in the map for.
     *
     * @return True if this map contains the key, false otherwise.
     */
    public boolean containsKey(Object key)
    {
        return dataMap.containsKey(key);
    }

    /**
     * Returns true if this map maps one or more keys to the specified value.
     *
     * @param  value The value to check if this array contains.
     *
     * @return True if this map maps one or more keys to the specified value.
     */
    public boolean containsValue(Object value)
    {
        return dataMap.containsValue(value);
    }

    /**
     * Returns a set view of the mappings contained in this map.
     *
     * @return A set view of the mappings contained in this map.
     */
    public Set<Map.Entry<K, V>> entrySet()
    {
        return new AbstractSet<Map.Entry<K, V>>()
            {
                /**
                 * Gets an iterator over the set.
                 *
                 * @return An iterator over the set.
                 */
                public Iterator<Map.Entry<K, V>> iterator()
                {
                    return new Iterator<Map.Entry<K, V>>()
                        {
                            Iterator<K> keyIterator = dataMap.keySet().iterator();

                            public boolean hasNext()
                            {
                                return keyIterator.hasNext();
                            }

                            public Map.Entry<K, V> next()
                            {
                                final K key = keyIterator.next();
                                final V data = get(key);

                                return new Map.Entry<K, V>()
                                    {
                                        public K getKey()
                                        {
                                            return key;
                                        }

                                        public V getValue()
                                        {
                                            return data;
                                        }

                                        public V setValue(V v)
                                        {
                                            throw new NotImplementedException();
                                        }
                                    };
                            }

                            public void remove()
                            {
                                throw new NotImplementedException();
                            }
                        };
                }

                /**
                 * Checks if the set contains the specified object.
                 *
                 * @param o The object to check if the set contains.
                 */
                public boolean contains(Object o)
                {
                    return dataMap.containsValue(o);
                }

                /**
                 * Removes the specified object from the set.
                 *
                 * @param  o The object to remove.
                 *
                 * @return True if an object was removed and false if not.
                 */
                public boolean remove(Object o)
                {
                    Object removed = KeyQueuedMap.this.remove(o);

                    return removed != null;
                }

                /**
                 * Gets the size of the set.
                 *
                 * @return The size of the set.
                 */
                public int size()
                {
                    return KeyQueuedMap.this.size();
                }

                /** Removes everything from the set. */
                public void clear()
                {
                    KeyQueuedMap.this.clear();
                }
            };
    }

    /**
     * Compares the specified object with this map for equality.
     *
     * @param  o The object to compare to.
     *
     * @return True if this hash array exactly equals the test one.
     */
    public boolean equals(Object o)
    {
        if (this == o)
        {
            return true;
        }

        if ((o == null) || (getClass() != o.getClass()))
        {
            return false;
        }

        KeyQueuedMap that = (KeyQueuedMap) o;

        return !((dataMap != null) ? (!dataMap.equals(that.dataMap)) : (that.dataMap != null)) &&
            !((keys != null) ? (!keys.equals(that.keys)) : (that.keys != null));

    }

    /**
     * Returns the value to which this map maps the specified key.
     *
     * @param  key The key to get the value from this map for.
     *
     * @return The object stored under the specified key, or null if the key is not in this map.
     */
    public V get(Object key)
    {
        return dataMap.get(key);
    }

    /**
     * Returns a hash code for this map.
     *
     * @return A hash code for this map.
     */
    public int hashCode()
    {
        return dataMap.hashCode();
    }

    /**
     * Returns true if this map contains no key-value mappings.
     *
     * @return True if this map contains no key-value mappings.
     */
    public boolean isEmpty()
    {
        return dataMap.isEmpty();
    }

    /**
     * Returns a set view of the keys contained in this map.
     *
     * @return A set view of the keys contained in this map.
     */
    public Set<K> keySet()
    {
        return dataMap.keySet();
    }

    /**
     * Associates the specified value with the specified key in this map.
     *
     * @param  key   The key to store against in this map.
     * @param  value The value to store against the key.
     *
     * @return The value that was previously stored against the key, or null if there was none.
     */
    public V put(K key, V value)
    {
        // Remove any existing matching key from the data
        V removedObject = remove(key);

        // Insert the data into the map.
        dataMap.put(key, value);

        // If the key is fresh, enqueue it.
        if (removedObject != null)
        {
            keys.offer(key);
        }

        // Return the replaced value if there was one
        return removedObject;
    }

    /**
     * Copies all of the mappings from the specified map to this map.
     *
     * @param t The map to copy into this one.
     */
    public void putAll(Map<? extends K, ? extends V> t)
    {
        for (Map.Entry<? extends K, ? extends V> entry : t.entrySet())
        {
            this.put(entry.getKey(), entry.getValue());
        }
    }

    /**
     * Removes the mapping for the specified key from this map if present.
     *
     * @param  key The key to remove from this map.
     *
     * @return The value that stored at that key, or null if there was none.
     */
    public V remove(Object key)
    {
        return dataMap.remove(key);
    }

    /**
     * Returns the number of key-value mappings in this map.
     *
     * @return The size of this map.
     */
    public int size()
    {
        return dataMap.size();
    }

    /**
     * Returns a collection view of the value contained in this map.
     *
     * @return The collection of values in this map.
     */
    public Collection<V> values()
    {
        return dataMap.values();
    }

    /**
     * Prints out the contents of the map, usefull for debugging.
     *
     * @return A string of the index and key/value pairs in this map.
     */
    public String toString()
    {
        String result = "KeyQueuedMap: [";

        // Cycle through all the indexes
        for (Iterator<Map.Entry<K, V>> i = dataMap.entrySet().iterator(); i.hasNext();)
        {
            // This will be used to hold the key found to match the current index
            Map.Entry<K, V> entry = i.next();
            Object nextKey = entry.getKey();
            Object nextData = entry.getValue();

            // Write out (key -> value) into the output string
            result += (nextKey + "-> " + nextData + (i.hasNext() ? ", " : ""));
        }

        result += "]";

        return result;
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed 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 com.thesett.common.util.doublemaps;

import java.util.HashMap;
import java.util.Map;

/**
 * HashMapXY defines a data structure associating data with a pair of x and y coordinates in 2 dimensions. To allow
 * fairly massive data structures (or very sparse) the x and y coordinates are specified as long integers (64 bits).
 *
 *
HashMapXY is a DoubleKeyedMap data structure based on a hash table with bucketing. 2D arrays of coordinates are
 * stored within a HashMap keyed by coordinates modulo the size of the 2D array. This creates a compromise between the
 * constant time access of the arrays and the ability of the hash table to store disconnected regions of data in sparser
 * maps without wasting storage space and with near constant time access. The size of the buckets may be configured when
 * the data structure is created to select the best compromise for a particular usage. Smaller buckets means less wasted
 * space but performance closer to that of using simple hash table representation.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Clear the whole map
 *
Check if the map is empty
 *
Report the number of object stored in the map
 *
Insert an object into the map
 *
Retrieve an associated object from the map
 *

 *
 * @author Rupert Smith
 */
public class HashMapXY<E> implements DoubleKeyedMap<Long, Long, E>
{
    /** The underlying map of data regions. */
    Map<Coordinates, Bucket<E>> regions;

    /** A counter used to record the number of items in the data structure. */
    int size;

    /**
     * The bucket size to use. Note that this is the size of one edge of a 2D bucket so the number of items in the
     * bucket will be the square of this value.
     */
    int bucketSize;

    /**
     * Builds a new HashMapXY.
     *
     * @param bucketSize The size of the buckets to use. Note that this is the size of one edge of a 2D bucket so the
     *                   number of items in the bucket will be the square of this value.
     */
    public HashMapXY(int bucketSize)
    {
        // Initialize the data structure
        regions = new HashMap<Coordinates, Bucket<E>>();
        size = 0;
        this.bucketSize = bucketSize;
    }

    /** Removes all mappings from this map. */
    public void clear()
    {
        // Re-initialize the data structure
        regions = new HashMap<Coordinates, Bucket<E>>();
        size = 0;
    }

    /**
     * Returns true if this map contains no coordinate-value mappings.
     *
     * @return true if this map contains no coordinate-value mappings.
     */
    public boolean isEmpty()
    {
        return size == 0;
    }

    /**
     * Returns the number of coordinate-value mappings in this map. If the map contains more than <tt>
     * Integer.MAX_VALUE</tt> elements, returns <tt>Integer.MAX_VALUE</tt>.
     *
     * @return the number of coordinate-value mappings in this map.
     */
    public int size()
    {
        return size;
    }

    /**
     * Returns <tt>true</tt> if this map contains a mapping for the specified coordinates.
     *
     * @param  x The X coordinate.
     * @param  y The Y coordinate.
     *
     * @return True if this map contains a mapping for the specified coordinate.
     */
    public boolean containsKey(Long x, Long y)
    {
        // Extract the region containing the coordinates from the hash table
        Bucket region = (Bucket) regions.get(new Coordinates(div(x, bucketSize), div(y, bucketSize)));

        // Check if the whole region is empty
        if (region == null)
        {
            return false;
        }

        // Check if the coordinates within the region contain data
        return (region.array[mod(x, bucketSize)][mod(y, bucketSize)] != null);
    }

    /**
     * Associates the specified value with the specified coordinate in this map (optional operation). If the map
     * previously contained a mapping for this coordinate, the old value is replaced.
     *
     * @param  x     The x coordinate.
     * @param  y     The y coordinate.
     * @param  value Value to be associated with the specified coordinate.
     *
     * @return Previous value associated with specified coordinate, or <tt>null</tt> if there was no mapping for
     *         coordinate.
     *
     * @throws NullPointerException If the specified value is <tt>null</tt>.
     */
    public E put(Long x, Long y, E value)
    {
        // Check that the value is not null as this data structure does not allow nulls
        if (value == null)
        {
            throw new IllegalArgumentException("Null values not allowed in HashMapXY data structure.");
        }

        // Extract the region containing the coordinates from the hash table
        Bucket<E> region = (Bucket<E>) regions.get(new Coordinates(div(x, bucketSize), div(y, bucketSize)));

        // Check if the region does not exist yet and create it if so
        if (region == null)
        {
            region = new Bucket<E>(bucketSize);
            regions.put(new Coordinates(div(x, bucketSize), div(y, bucketSize)), region);
        }

        // Take a reference to the old value if there is one
        E old = region.array[mod(x, bucketSize)][mod(y, bucketSize)];

        // Insert the new value into the bucket
        region.array[mod(x, bucketSize)][mod(y, bucketSize)] = value;

        // Increment the bucket and whole data structure item counts to reflect the true size
        size++;
        region.itemCount++;

        // Return the replaced value
        return old;
    }

    /**
     * Returns the value to which this map maps the specified coordinate. Returns <tt>null</tt> if the map contains no
     * mapping for this coordinate. A return value of <tt>null</tt> indicates that the map contains no mapping for the
     * coordinate.
     *
     * @param  x The x coordinate.
     * @param  y The y coordinate.
     *
     * @return The value to which this map maps the specified coordinate, or <tt>null</tt> if the map contains no
     *         mapping for this coordinate.
     */
    public E get(Long x, Long y)
    {
        // Extract the region containing the coordinates from the hash table
        Bucket<E> region = regions.get(new Coordinates(div(x, bucketSize), div(y, bucketSize)));

        // Check if the whole region is empty
        if (region == null)
        {
            return null;
        }

        // Get the coordinates within the region
        return (region.array[mod(x, bucketSize)][mod(y, bucketSize)]);
    }

    /**
     * Removes the mapping for this coordinate from this map if present (optional operation).
     *
     * @param  x The x coordinate.
     * @param  y The y coordinate.
     *
     * @return Previous value associated with specified coordinate, or <tt>null</tt> if there was no mapping for
     *         coordinate.
     */
    public E remove(Long x, Long y)
    {
        // Extract the region containing the coordinates from the hash table
        Bucket<E> region = (Bucket<E>) regions.get(new Coordinates(div(x, bucketSize), div(y, bucketSize)));

        // Check if the whole region is empty
        if (region == null)
        {
            // Return null as nothing to remove
            return null;
        }

        // Get the coordinates within the region
        E removed = (region.array[mod(x, bucketSize)][mod(y, bucketSize)]);

        // Clear the coordinate within the region
        region.array[mod(x, bucketSize)][mod(y, bucketSize)] = null;

        // Decrement the bucket and whole data strucutre item counts to reflect the true size
        size--;
        region.itemCount--;

        // Return the removed data item
        return removed;
    }

    /**
     * Calculates the modulo of a coordinate with the bucket size. Correctly calculates this module for negative
     * coordinates such the the first negative bucket is -1 with element 0 corresponding to -100 running to 99
     * corresponding to -1.
     *
     * @param  c          The coordinate value.
     * @param  bucketSize The bucket size.
     *
     * @return The coordinate offset into the bucket modulo the bucket size.
     */
    private int mod(long c, int bucketSize)
    {
        return (int) ((c < 0) ? ((bucketSize + (c % bucketSize)) % bucketSize) : (c % bucketSize));
    }

    /**
     * Calculates the bucket size divided into the coordinate to generate the bucket number. Correctly calculates this
     * for negative coordinates such that bucket -1 runs from -100 to -1, -2 runs from -200 to -101 and so on.
     *
     * @param  c          The coordinate value.
     * @param  bucketSize The bucket size.
     *
     * @return The bucket number to find the coordinate in.
     */
    private long div(long c, int bucketSize)
    {
        return ((c < 0) ? ((c - (bucketSize - 1)) / bucketSize) : (c / bucketSize));
    }

    /**
     * Bucket defines a data structure used to hold the contents of a bucket. This is simply a 2D array but a data
     * structure is used to hold additional properties of the array such as the number of items held in it. This is
     * useful when deciding whether or not the array is empty.
     *
     *

CRC Card

    *
ResponsibilitiesCollaborations
     *
     *

     * Hold a two dimensional array of data.
     *

     */
    private static class Bucket<E>
    {
        /** The data array. */
        public E[][] array;

        /** The number of data items held in the array. */
        public int itemCount;

        /**
         * Creates a new Bucket of the specified size.
         *
         * @param size The side-size of bucket to create.
         */
        public Bucket(int size)
        {
            // Initialize the bucket
            array = (E[][]) new Object[size][size];
            itemCount = 0;
        }
    }

    /**
     * Coordinates defines a simple data structure that holds a pair of long x and y coordinates. This is used as the
     * key in the hash table to store data regions against. This class also defines hash and equals methods for the
     * coordinates for use within the hash table hashing mechanism.
     *
     *

CRC Card

     *
ResponsibilitiesCollaborations
     *
Hold a pair of integer X and Y coordinates.
     *

     */
    private static class Coordinates
    {
        /** The X coordinate. */
        public long x;

        /** The Y coordinate. */
        public long y;

        /**
         * Creates a new Coordinates object.
         *
         * @param x The X coord.
         * @param y The Y coord.
         */
        public Coordinates(long x, long y)
        {
            this.x = x;
            this.y = y;
        }

        /**
         * Checks if this coordinate equals another.
         *
         * @param  o The object to compare to.
         *
         * @return True if the object is a coordinate equal to this one.
         */
        public boolean equals(Object o)
        {
            // Check that the comparitor is a Coordinate
            if (!(o instanceof Coordinates))
            {
                return false;
            }

            Coordinates compare = (Coordinates) o;

            // Check if the coordinates are identical
            return (compare.x == x) && (compare.y == y);
        }

        /**
         * Computes a hash code from the X and Y coordinates.
         *
         * @return A hash code computed from the X and Y coordinates.
         */
        public int hashCode()
        {
            return (int) ((x ^ (x >> 32)) + (y ^ (y >> 32)));
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed 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 com.thesett.common.error;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Wraps multiple user readable errors in a single exception.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *

 *
 * @author Rupert Smith
 */
public class MultipleUserErrorException extends UserReadableException
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(MultipleUserErrorException.class.getName()); */

    /** Holds the user readable error messages. */
    List<UserReadableError> errors = new ArrayList<UserReadableError>();

    /**
     * Builds an exception with a message and a user message and a message key. To create an exception with no key and
     * just a user message or with no user message at all pass in null arguments for the key or user message.
     *
     * @param message     The exception message.
     * @param cause       The wrapped exception underlying this one.
     * @param key         A key to look up user readable messages with.
     * @param userMessage The user readable message or data string.
     */
    public MultipleUserErrorException(String message, Throwable cause, String key, String userMessage)
    {
        super(message, cause, key, userMessage);

        errors.add(this);
    }

    /**
     * Builds an exception with a message and a user message and a message key. To create an exception with no key and
     * just a user message or with no user message at all pass in null arguments for the key or user message.
     *
     * @param message The exception message.
     * @param cause   The wrapped exception underlying this one.
     * @param errors  A list of many user readable errors.
     */
    public MultipleUserErrorException(String message, Throwable cause, Collection<UserReadableError> errors)
    {
        super(message, cause, null, null);

        /*log.fine(
            " public MultipleUserErrorException(String message, Throwable cause, List<UserReadableError> errors): called");*/

        for (UserReadableError error : errors)
        {
            /*log.fine("next error = " + error.getUserMessageKey());*/
        }

        errors.addAll(errors);
    }

    /**
     * Adds another user readable error message to this exception.
     *
     * @param key         A key to look up user readable messages with.
     * @param userMessage The user readable message or data string.
     */
    public void addErrorMessage(String key, String userMessage)
    {
        /*log.fine("addErrorMessage(String key, String userMessage): called");*/
        /*log.fine("userMessage = " + userMessage);*/

        errors.add(new UserReadableErrorImpl(key, userMessage));
    }

    /**
     * Gets all the user readable error messages.
     *
     * @return All the user readable error messages.
     */
    public List<UserReadableError> getErrors()
    {
        /*log.fine("public List<UserReadableError> getErrors(): called");*/

        return errors;
    }
}