/*
 * 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;

import java.lang.reflect.Array;
import java.util.Comparator;
import java.util.Iterator;

import com.thesett.common.error.NotImplementedException;

/**
 * A Fibonacci heap (F-heap) is a collection of heap-ordered trees. F-heaps are a type of data structure in which the
 * work that must be done to reorder the structure is postponed until the very last possible moment. F-heaps are useful
 * for algorithms involving graph data structures, such as those used for computing shortest paths in computer networks.
 * The common operations supported by F-heaps are insert, find minimum, extract minimum and merge. These operations are
 * described below:
 *
 *

 *
Insert. A new node is inserted into the heap by simply adding the node to the root list and updating the pointer
 * to the minimum node if necessary.

 *
Find minimum. This operation simply returns the node in the heap with the minimum key. This node is constantly
 * referenced, so no real work must be done here.

 *
Merge. Two Fibonacci heaps are joined into one by simply combining the two circular root lists into one larger,
 * circular root list. The mimimum node is set to the smaller of the minimums of the two heaps.

 *
Extract minimum. First, each of the minimum node's children is added to the root list and the minimum node itself
 * is deleted from the list. Finally, roots of equal degree in the root list are combined until at most one root of each
 * degree remains.

 *

 *
 *
Operations on F-heaps can be classified into two categories, those that involve deleting an element and those that
 * do not; with the former class of operations running in amortized O(log(n)) time and the latter in O(1) time.
 * Although, F-heaps are relatively complex to code and do not support quick searching, they have advantages over other
 * types of heaps because they are the only known data structure that can perform the decrease-key operation in constant
 * amortized time.
 *
 *
The kth order binomial tree, Bk, is defined recursilvely. B0 is a single node. For all k > 0, Bk consists of two
 * copies of Bk-1 that have been linked together, meaning that the root of one Bk-1 has become a new child of the other
 * root. Binomial trees have the following useful properties:
 *
 *

 *
The root of Bk has degree k.

 *
The children of the root of Bk are the roots of B0, B1, ..., Bk-1.

 *
Bk has height k.

 *
Bk has 2^k nodes.

 *
Bk can be obtained from Bk-1 by adding a new child to every node.

 *
Bk has k-choose-d nodes at depth d, for all 0 <= d <= k.

 *
Bk has 2^(k-h-1) nodes with height h, for all 0 <= h <= k, and one node (the root) with height k.

 *

 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Insert elements onto the heap.
 *
Report the minimum element very efficiently.
 *
Combine two heaps into one.
 *
Remove the minimum element.
 *

 *
 * @author Rupert Smith
 * @todo   Add the decrease key and delete operations. The caller must correctly identify the node to be
 *         decreased/removed. Probably the best way to do this is to add another version of the offer method that return
 *         the node for new elements inserted. It is then up to the caller to keep track of this node for later
 *         decrease/remove operations if it needs to do them.
 */
public class FibonacciHeap<E> extends AbstractHeap<E>
{
    /** Node annotations. See {@link Node} for details. */
    private enum Annotation
    {
        /** Used to mark a node in the heap. */
        Marked,

        /** Used to decrease a node to minus infinity in the heap prior to deleting it. */
        MinusInfinity
    }

    /** Holds a reference to the minimum node on the heap. This is always maintained for fast access. */
    private Node minNode;

    /** Builds a heap that is ordered on the natural ordering of its elements. */
    public FibonacciHeap()
    {
        super(null);
    }

    /**
     * Builds a heap that is ordered by the specified comparator.
     *
     * @param comparator The comparator to use to order the heap elements.
     */
    public FibonacciHeap(Comparator<? super E> comparator)
    {
        super(comparator);
    }

    /**
     * Returns an iterator over the elements in this heap.
     *
     * @return An iterator over the elements in this heap.
     */
    public Iterator<E> iterator()
    {
        throw new NotImplementedException();
    }

    /**
     * Inserts the specified element into this heap.
     *
     * @param  o The data element to add to the heap.
     *
     * @return True if it was succesfully added, false if not. (This implmentation always returns true).86
     */
    public boolean offer(E o)
    {
        // Make a new node out of the new data element.
        Node newNode = new Node(o);

        // Check if there is already a minimum element.
        if (minNode != null)
        {
            // There is already a minimum element, so add this new element to its right.
            newNode.next = minNode.next;
            newNode.prev = minNode;

            minNode.next.prev = newNode;
            minNode.next = newNode;

            // Compare the new element with the minimum and update the minimum if neccessary.
            updateMinimum(newNode);
        }

        // There is not already a minimum element.
        else
        {
            // Update the new element previous and next references to refer to itself so that it forms a doubly linked
            // list with only one element. This leaves the data structure in a suitable condition for adding more
            // elements.
            newNode.next = newNode;
            newNode.prev = newNode;

            // Set the minimum element to be the new data element.
            minNode = newNode;
        }

        // Increment the count of data elements in this collection.
        size++;

        // Return true to indicate that the new data element was accepted into the heap.
        return true;
    }

    /**
     * Retrieves, but does not remove, the (head) minimum element of this heap, returning null if this heap is empty.
     *
     * @return The head element of the hea, or null if the heap is empty.
     */
    public E peek()
    {
        // Return the minimum element
        return minNode.element;
    }

    /**
     * Retrieves and removes the (head) minimum element of this heap, or null if this heap is empty.
     *
     * @return The head element of the hea, or null if the heap is empty.
     */
    public E poll()
    {
        // Check if there is only one element in the heap.
        if (size == 1)
        {
            E result = minNode.element;

            // Set the minimum to null.
            minNode = null;

            // Decrease the count of elements in the heap.
            size--;

            return result;
        }

        // Check that there is a minimum element to return.
        else if (minNode != null)
        {
            // Get the minimum value that is to be returned.
            E result = minNode.element;

            // Promote the minimum nodes children into the root list but don't update their parent references. Updating
            // their parent references will be done later.
            if (minNode.degree > 0)
            {
                insertNodes(minNode, minNode.child);
            }

            // Cut the minimum node out of the root list and update the minimum to be old minimums next node. This
            // node may not really be the minimum but it is taken to be the initial candidate for a new minimum. The
            // real minimum will be scanned for later.
            minNode.next.prev = minNode.prev;
            minNode.prev.next = minNode.next;

            minNode = minNode.next;

            // The consolidation process will merge all the binomial trees of the same order until there are none
            // of duplicate size left. As each binomial tree of order k, Bk, holds 2^k nodes the biggest tree
            // possible for a heap of n nodes will be log to the base 2 of the next biggest power of 2 above or
            // equal to n. The next section of code creates an array with this many elements, indexed by the order
            // of the binomial tree, that is used to keep track of what tree orders currently exist during the
            // consolidation process.
            Node[] tree = (Node[]) Array.newInstance(minNode.getClass(), ceilingLog2(size - 1) + 1);

            // Loop through the root list, setting parent references to null and consolidating the remainder of the heap
            // into binomial trees. The loop begins at the next node after the min node and finishes on the min node
            // itself. This means that the min node is always the very last in the root list to be examined and
            // consolidated ensuring that it cannot have been removed from the root list before the loop gets to it. The
            // terminal min node is explicitly referenced because the consolidation process will update the min node
            // during the loop.
            Node nextNode = null;
            Node nextNextNode = minNode.next;
            Node terminalNode = minNode;

            do
            {
                // Move on to the next node.
                nextNode = nextNextNode;

                // Work out what the next next node will be at the start of the loop as manipulations durings
                // the loop may override the next node reference of the current next node.
                nextNextNode = nextNode.next;

                // Update parent references to null.
                nextNode.parent = null;

                // Update the minimum if the current root list element is smaller than the current best candidate.
                updateMinimum(nextNode);

                // Consolidate the remainder of the heap into binomial trees by merging duplicate trees of equal size.
                // Loop until no tree with the same size as the current one exists.
                int degree = nextNode.degree;
                Node parentNode = nextNode;

                while (tree[degree] != null)
                {
                    // Clear the binomial tree of this size from the tree array as it is about to be merged with the
                    // current node and a new tree of twice the size created.
                    Node mergeNode = tree[degree];

                    tree[degree] = null;

                    // Compare the roots of the two trees to be merged to decide which is the smaller and should form
                    // the single root of the merged tree.
                    if (compare(mergeNode, parentNode) < 0)
                    {
                        // Swap the two nodes.
                        Node temp = mergeNode;

                        mergeNode = parentNode;
                        parentNode = temp;
                    }

                    // Cut the tree rooted by the larger root node from the root list.
                    mergeNode.next.prev = mergeNode.prev;
                    mergeNode.prev.next = mergeNode.next;
                    mergeNode.next = mergeNode;
                    mergeNode.prev = mergeNode;

                    // Paste the tree rooted by the larger root node into the tree with the smaller root node.
                    mergeNode.parent = parentNode;

                    // Check if the smaller node (the parent) already has some children.
                    if (parentNode.child != null)
                    {
                        // Stitch the larger node into the circular doubly linked list of children.
                        insertNodes(parentNode.child, mergeNode);

                        // Update all the parent references of the newly added children.
                    }

                    // The smaller node does not already have children.
                    else
                    {
                        // Set the larger node (of degree 1) as its first child.
                        parentNode.child = mergeNode;

                        // Make sure the child node forms a doubly linked list with itself.
                        mergeNode.next = mergeNode;
                        mergeNode.prev = mergeNode;
                    }

                    // Bump up by one the order of the smaller root node to which the other tree of equal size was
                    // added.
                    parentNode.degree++;

                    // Continue the scan for duplicate trees on the next larger degree.
                    degree++;
                }

                tree[degree] = parentNode;
            }
            while (nextNode != terminalNode);

            /*
             * String out = "At End: ["; for (int i = 0; i < tree.length; i++) out += (i == tree.length - 1) ? tree[i] :
             * tree[i] + ", "; out += "]"; log.info(out);
             */

            // Decrease the count of elements in the heap.
            size--;

            // Return the minimum element.
            return result;
        }

        // There is no minimum element so return null.
        else
        {
            return null;
        }
    }

    /**
     * Calculates the smallest integer value, m, such that m^2 >= n. The ceiling log2 of n.
     *
     * @param  n The value to calulate ceiling log 2 of.
     *
     * @return The smallest integer value, m, such that m^2 >= n.
     *
     * @todo   This can possibly be done more neatly. Consult Hackers Delight or aggregate.org for the following
     *         algorithm: y = (x & (x - 1)); y |= -y; y >>= (WORDBITS - 1); x |= (x >> 1); x |= (x >> 2); x |= (x >> 4);
     *         x |= (x >> 8); x |= (x >> 16); #ifdef LOG0UNDEFINED return(ones(x) - 1 - y); #else return(ones(x >> 1) -
     *         y); #endif
     */
    private static int ceilingLog2(int n)
    {
        int oa;
        int i;
        int b;

        oa = n;
        b = 32 / 2;
        i = 0;

        while (b != 0)
        {
            i = (i << 1);

            if (n >= (1 << b))
            {
                n /= (1 << b);
                i = i | 1;
            }
            else
            {
                n &= (1 << b) - 1;
            }

            b /= 2;
        }

        if ((1 << i) == oa)
        {
            return i;
        }
        else
        {
            return i + 1;
        }
    }

    /**
     * Compares the specified node with the minimum and updates the minimum if neccessary. If a comparator was used to
     * create this heap then this comparator is used to perform the comparison. If no comparator was set then the
     * natural ordering of the element type is used. The element must implement the Comparable interface to support a
     * natural ordering. If it does not there will be a class cast exception thrown.
     *
     * @param  node the node to compare with the current minimum.
     *
     * @throws ClassCastException if the element in the specified node cannot be compared with elements currently in the
     *                            heap according to the heap's ordering.
     *
     * @todo   Use the compare method.
     */
    private void updateMinimum(Node node)
    {
        // Check if a comparator was set.
        if (entryComparator != null)
        {
            // Use the comparator to compare the candidate new minimum with the current one and check if the new one
            // should be set.
            if (entryComparator.compare(node.element, minNode.element) < 0)
            {
                // Update the minimum node.
                minNode = node;
            }
        }

        // No comparator was set so use the natural ordering.
        else
        {
            // Cast the candidate new minimum element into a Comparable and compare it with the existing minimum
            // to check if the new one should be set.
            if (((Comparable) node.element).compareTo(minNode.element) < 0)
            {
                // Update the minimum node.
                minNode = node;
            }
        }
    }

    /**
     * Compares two heap nodes. The comparison performed is dependant on whether a comparator has been set or the
     * natural ordering is to be used.
     *
     * @param  node1 The first node to compare.
     * @param  node2 The second node to compare.
     *
     * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than
     *         the specified object.
     */
    private int compare(Node node1, Node node2)
    {
        // Check if a comparator was set.
        if (entryComparator != null)
        {
            // Use the comparator to compare.
            return entryComparator.compare(node1.element, node2.element);
        }

        // No comparator was set so use the natural ordering.
        else
        {
            // Cast one of the elements into a Comparable and compare it with the other.
            return ((Comparable) node1.element).compareTo(node2.element);
        }
    }

    /**
     * Inserts a single node or a circular doubly linked list of nodes into a list next to the specified node. I does
     * not matter if the specified nodes are singletons or part of a chain as they will be correctly linked in in either
     * case so long as their prev and next references form a loop with themselves.
     *
     * @param node    the node or node within a chain that the new node chain is to be inserted next to.
     * @param newNode a single node or a node within a chain that is to be linked into the root list next to the minimum
     *                node.
     */
    private void insertNodes(Node node, Node newNode)
    {
        // Keep a reference to the next node in the node's chain as this will be overwritten when attaching the node
        // or chain into the root list.
        Node oldNodeNext = newNode.next;

        // Break open the node's chain and attach it into the root list.
        newNode.next.prev = node;
        newNode.next = node.next;

        // Break open the root list chain and attach it to the new node or chain.
        node.next.prev = newNode;
        node.next = oldNodeNext;
    }

    /**
     * Every heap node maintains a reference to (up to) four other elements: its parent, one of its children, the next
     * node and the previous node. The root node has no parent and leaf nodes have no children. The next and previous
     * nodes are nodes at the same level in the heap or one of its binomial trees. All such nodes are kept in a circular
     * doubly linked list.
     *
     *
Nodes may be specially annotated when keys are decreased or deleted. Deletion is equivalent to decreasing the
     * key to minus infinity and then extracting the minimum. An annotation is used to tag a key as being minus
     * infinity. When a key is decreased its parent must be marked to indicate that one of its children has been
     * promoted, or if it is already marked then it must also be promoted and the mark removed.
     */
    private class Node
    {
        /** The data element. */
        E element;

        /**
         * The degree of this node. This is the number of children it has but not including grand-children or below.
         * This is also equal to the order of the binomial tree that this node head forms the root of. See the class
         * level comment for {@link FibonacciHeap} for a reminder of the properties of binomial trees.
         */
        int degree;

        /** Used to hold the parent of this node. */
        Node parent;

        /** Used to hold the first child of this node. */
        Node child;

        /** Used to hold the previous node in the circular list at the same level as this node. */
        Node prev;

        /** Used to hold the next node in the circular list at the same level as this node. */
        Node next;

        /** Used to indicate special annotations on this node. */
        Annotation annotation;

        /**
         * Creates a new Node object for a given data element.
         *
         * @param element The data element to create a node for.
         */
        Node(E element)
        {
            this.element = element;
        }

        // public String toString() { return "" + degree; }// + " " + element; }
    }
}


/*
 * 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.logic.fol.wam.debugger.monitor;

import java.beans.PropertyChangeListener;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * BreakpointMonitor listens for updates to the "IP" register, which will occur every time a breakpoint (or step)
 * occurs.
 *
 *
Once a break-point has been hit, the thread that is running the virtual machine will be paused by invoking the
 * {@link #pause()} method. The {@link #release()} method should release any paused thread and allow it to continue
 * running.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Watch for changes to the IP register.

 *
 Block and release execution threads.

 *

 *
 * @author Rupert Smith
 */
public abstract class BreakpointMonitor implements PropertyChangeListener
{
    Lock lock = new ReentrantLock();
    Condition released = lock.newCondition();

    /** Pauses any calling thread, until {@link #released} is invoked. */
    public void pause()
    {
        try
        {
            lock.lock();

            try
            {
                released.await();
            }
            catch (InterruptedException e)
            {
                // Exception set to null as compensation action of returning immediately with current thread interrupted
                // is taken.
                e = null;
                Thread.currentThread().interrupt();

                return;
            }
        }
        finally
        {
            lock.unlock();
        }
    }

    /** Releases any threads waiting in the {@link #pause()} method. */
    public void release()
    {
        try
        {
            lock.lock();

            released.signalAll();
        }
        finally
        {
            lock.unlock();
        }
    }
}


/*
 * 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;

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

/**
 * TimeoutMap is an implementation of {@link java.util.Map} that times out its entries after a specified period using a
 * mark and sweep caching algorithm. As the map implements the {@link java.util.Map} interface it can be used as any
 * other Map can in order to implement a cache. It has the advantage that the cache elements automatically time out and
 * are periodically removed from the cache without any code having to be written to do this explicitly.
 *
 *
This class protects its underlying cache implementation from conflicting concurrent accesses in a multi-threaded
 * environment by synchronizing on the underlying cache. This is necessary to ensure that the cache sweep thread does
 * not conflict with any cache accesses. It also has the benefit that the cache can be used confidently in a
 * multi-threaded environment.
 *
 *
All timings used in this class are represented as long values in milliseconds.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *

 *
 * @author Rupert Smith
 */
public class TimeoutMap implements Map
{
    /** Used for logging purposes. */
    /* private static final Logger log = Logger.getLogger(TimeoutMap.class.getName()); */

    /** The time between cache sweeps. */
    private final long sweepTime;

    /** The maximum age of an untouched entry in the cache before it is removed. */
    private final long sweepExpiryTime;

    /** The cache sweeping thread. Reference kept here so that it can be woken up when needed. */
    private Thread cacheSweepThread;

    /** A flag used to indicate to the sweep thread that it should stop running. */
    private boolean sweepThreadKillFlag;

    /** A cache of elements. */
    private final Map cache = new HashMap();

    /** A heap of cache entries marked by the cache sweep operation. */
    private final Map marked = new HashMap();

    /**
     * Creates a timeout map with the specified sweeping interval and maximum time out.
     *
     * @param sweepTime  The time in milliseconds between cache sweeps.
     * @param expiryTime The maximum age of cache elements before they expire.
     */
    public TimeoutMap(long sweepTime, long expiryTime)
    {
        // Keep the specified sweep and expiry time.
        this.sweepTime = sweepTime;
        this.sweepExpiryTime = expiryTime;

        // Clear the sweep thread kill flag.
        sweepThreadKillFlag = false;

        // Start the sweep thread running with low priority.
        cacheSweepThread =
            new Thread()
            {
                public void run()
                {
                    sweep();
                }
            };

        cacheSweepThread.setPriority(Thread.MIN_PRIORITY);
        cacheSweepThread.start();
    }

    /** Stops the sweep algorithm from running. */
    public void kill()
    {
        // Set the sweep thread kill flag.
        sweepThreadKillFlag = true;

        // Wake up the sweep thread so that it reads its kill flag.
        cacheSweepThread.interrupt();
    }

    /** Restarts the sweep alogirithm. Useful after a kill has stopped it. */
    public void restart()
    {
        // Clear the sweep thread kill flag.
        sweepThreadKillFlag = false;

        // Start the sweep thread running with low priority.
        cacheSweepThread =
            new Thread()
            {
                public void run()
                {
                    sweep();
                }
            };

        cacheSweepThread.setPriority(Thread.MIN_PRIORITY);
        cacheSweepThread.start();
    }

    /**
     * Returns the number of key-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 key-value mappings in this map.
     */
    public int size()
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            return cache.size();
        }
    }

    /**
     * Returns <tt>true</tt> if this map contains no key-value mappings.
     *
     * @return <tt>true</tt> if this map contains no key-value mappings.
     */
    public boolean isEmpty()
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            return cache.isEmpty();
        }
    }

    /**
     * Returns <tt>true</tt> if this map contains a mapping for the specified key.
     *
     * @param  key Key whose presence in this map is to be tested.
     *
     * @return <tt>true</tt> if this map contains a mapping for the specified key.
     *
     * @throws ClassCastException   If the key is of an inappropriate type for this map.
     * @throws NullPointerException If the key is <tt>null</tt> and this map does not permit <tt>null</tt> keys.
     */
    public boolean containsKey(Object key)
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            return cache.containsKey(key);
        }
    }

    /**
     * Returns <tt>true</tt> if this map maps one or more keys to the specified value. More formally, returns <tt>
     * true</tt> if and only if this map contains at least one mapping to a value <tt>v</tt> such that <tt>(value==null
     * ? v==null : value.equals(v))</tt>. This operation will probably require time linear in the map size for most
     * implementations of the <tt>Map</tt> interface.
     *
     * @param  value Value to test the presence of in this map.
     *
     * @return <tt>true</tt> if this map maps one or more keys to the specified value.
     */
    public boolean containsValue(Object value)
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            // Look for a matching ElementMonitor in the cache. The comparison operation of ElementMonitor will ignore
            // the zero time stamp in the comparison ElementMonitor created here.
            return cache.containsValue(new ElementMonitor(value, 0L));
        }
    }

    /**
     * Returns the value to which this map maps the specified key. Returns <tt>null</tt> if the map contains no mapping
     * for this key. A return value of <tt>null</tt> does not necessarily indicate that the map contains no
     * mapping for the key; it's also possible that the map explicitly maps the key to <tt>null</tt>. The <tt>
     * containsKey</tt> operation may be used to distinguish these two cases.
     *
     *
More formally, if this map contains a mapping from a key <tt>k</tt> to a value <tt>v</tt> such that <tt>
     * (key==null ? k==null : key.equals(k))</tt>, then this method returns <tt>v</tt>; otherwise it returns <tt>
     * null</tt>. (There can be at most one such mapping.)
     *
     * @param  key Key whose associated value is to be returned.
     *
     * @return The value to which this map maps the specified key, or <tt>null</tt> if the map contains no mapping for
     *         this key.
     *
     * @throws ClassCastException   If the key is of an inappropriate type for this map (optional).
     * @throws NullPointerException If the key is <tt>null</tt> and this map does not permit <tt>null</tt> keys
     *                              (optional).
     *
     * @see    #containsKey(Object)
     */
    public Object get(Object key)
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            // Try to extract the matching element as an ElementMonitor,
            ElementMonitor monitor = (ElementMonitor) cache.get(key);

            // If the element is not null then return its value, else null.
            // Also upgrade the timestamp of the matching element to the present to show that it has been recently
            // accessed.
            if (monitor != null)
            {
                // Upgrade the timestamp.
                long t = System.currentTimeMillis();
                monitor.lastTouched = t;

                // Return the element.
                return monitor.element;
            }
            else
            {
                return null;
            }
        }
    }

    /**
     * Associates the specified value with the specified key in this map (optional operation). If the map previously
     * contained a mapping for this key, the old value is replaced by the specified value. (A map <tt>m</tt> is said to
     * contain a mapping for a key <tt>k</tt> if and only if {@link #containsKey(Object) m.containsKey(k)} would return
     * <tt>true</tt>.))
     *
     * @param  key   Key with which the specified value is to be associated.
     * @param  value Value to be associated with the specified key.
     *
     * @return Previous value associated with specified key, or <tt>null</tt> if there was no mapping for key. A <tt>
     *         null</tt> return can also indicate that the map previously associated <tt>null</tt> with the specified
     *         key, if the implementation supports <tt>null</tt> values.
     *
     * @throws UnsupportedOperationException If the <tt>put</tt> operation is not supported by this map.
     * @throws ClassCastException            If the class of the specified key or value prevents it from being stored in
     *                                       this map.
     * @throws IllegalArgumentException      If some aspect of this key or value prevents it from being stored in this
     *                                       map.
     * @throws NullPointerException          If this map does not permit <tt>null</tt> keys or values, and the specified
     *                                       key or value is <tt>null</tt>.
     */
    public Object put(Object key, Object value)
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            // Create a new ElementMonitor in the cache for the new element.
            // Timestamp the new element with the present time.
            long t = System.currentTimeMillis();

            // Extract the element value out of the replaced element monitor if any.
            ElementMonitor replaced = (ElementMonitor) cache.put(key, new ElementMonitor(value, t));

            if (replaced != null)
            {
                return replaced.element;
            }
            else
            {
                return null;
            }
        }
    }

    /**
     * Removes the mapping for this key from this map if it is present (optional operation). More formally, if this map
     * contains a mapping from key <tt>k</tt> to value <tt>v</tt> such that (key==null ? k==null :
     * key.equals(k)), that mapping is removed. (The map can contain at most one such mapping.)
     *
     *
Returns the value to which the map previously associated the key, or <tt>null</tt> if the map contained no
     * mapping for this key. (A <tt>null</tt> return can also indicate that the map previously associated <tt>null</tt>
     * with the specified key if the implementation supports <tt>null</tt> values.) The map will not contain a mapping
     * for the specified key once the call returns.
     *
     * @param  key key whose mapping is to be removed from the map.
     *
     * @return previous value associated with specified key, or <tt>null</tt> if there was no mapping for key.
     *
     * @throws ClassCastException            if the key is of an inappropriate type for this map (optional).
     * @throws NullPointerException          if the key is <tt>null</tt> and this map does not permit <tt>null</tt> keys
     *                                       (optional).
     * @throws UnsupportedOperationException if the <tt>remove</tt> method is not supported by this map.
     */
    public Object remove(Object key)
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            // Remove the element from the marked heap (if it exists in the heap) and from the cache.
            marked.remove(key);

            return cache.remove(key);
        }
    }

    /**
     * Copies all of the mappings from the specified map to this map (optional operation). The effect of this call is
     * equivalent to that of calling {@link #put(Object,Object) put(k, v)} on this map once for each mapping from key
     * <tt>k</tt> to value <tt>v</tt> in the specified map. The behavior of this operation is unspecified if the
     * specified map is modified while the operation is in progress.
     *
     * @param  t Mappings to be stored in this map.
     *
     * @throws UnsupportedOperationException if the <tt>putAll</tt> method is not supported by this map.
     * @throws ClassCastException            if the class of a key or value in the specified map prevents it from being
     *                                       stored in this map.
     * @throws IllegalArgumentException      some aspect of a key or value in the specified map prevents it from being
     *                                       stored in this map.
     * @throws NullPointerException          if the specified map is <tt>null</tt>, or if this map does not permit <tt>
     *                                       null</tt> keys or values, and the specified map contains <tt>null</tt> keys
     *                                       or values.
     */
    public void putAll(Map t)
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            // Iterate over all elements in the map to add, placing each of them into an ElementMonitor before
            // putting them into the cache.
            for (Object nextKey : t.keySet())
            {
                Object nextValue = t.get(nextKey);

                // Delegate to the put method to wrap the new item in an ElementMonitor.
                cache.put(nextKey, nextValue);
            }
        }
    }

    /**
     * Removes all mappings from this map (optional operation).
     *
     * @throws UnsupportedOperationException clear is not supported by this map.
     */
    public void clear()
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            // Clear the cache and the marked heap.
            marked.clear();
            cache.clear();
        }
    }

    /**
     * Returns a set view of the keys contained in this map. The set is backed by the map, so changes to the map are
     * reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress
     * (except through the iterator's own <tt>remove</tt> operation), the results of the iteration are undefined. The
     * set supports element removal, which removes the corresponding mapping from the map, via the <tt>
     * Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt> <tt>retainAll</tt>, and <tt>clear</tt> operations.
     * It does not support the add or <tt>addAll</tt> operations.
     *
     * @return a set view of the keys contained in this map.
     */
    public Set keySet()
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            return cache.keySet();
        }
    }

    /**
     * Returns a collection view of the values contained in this map. The collection is backed by the map, so changes to
     * the map are reflected in the collection, and vice-versa. If the map is modified while an iteration over the
     * collection is in progress (except through the iterator's own <tt>remove</tt> operation), the results of the
     * iteration are undefined. The collection supports element removal, which removes the corresponding mapping from
     * the map, via the <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
     * <tt>clear</tt> operations. It does not support the add or <tt>addAll</tt> operations.
     *
     * @return a collection view of the values contained in this map.
     */
    public Collection values()
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            return cache.values();
        }
    }

    /**
     * Returns a set view of the mappings contained in this map. Each element in the returned set is a {@link Map.Entry}
     * . The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is
     * modified while an iteration over the set is in progress (except through the iterator's own <tt>remove</tt>
     * operation, or through the <tt>setValue</tt> operation on a map entry returned by the iterator) the results of the
     * iteration are undefined. The set supports element removal, which removes the corresponding mapping from the map,
     * via the <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt>
     * operations. It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
     *
     * @return a set view of the mappings contained in this map.
     */
    public Set entrySet()
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            return cache.entrySet();
        }
    }

    /**
     * Compares the specified object with this map for equality. Returns <tt>true</tt> if the given object is also a map
     * and the two Maps represent the same mappings. More formally, two maps <tt>t1</tt> and <tt>t2</tt> represent the
     * same mappings if <tt>t1.entrySet().equals(t2.entrySet())</tt>. This ensures that the <tt>equals</tt> method works
     * properly across different implementations of the <tt>Map</tt> interface.
     *
     * @param  o object to be compared for equality with this map.
     *
     * @return <tt>true</tt> if the specified object is equal to this map.
     */
    public boolean equals(Object o)
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            // Check that the comparator is also a TimeoutMap.
            if (o instanceof TimeoutMap)
            {
                TimeoutMap tm = (TimeoutMap) o;

                // Check the underlying caches are identical.
                return tm.cache.equals(cache);
            }
            else
            {
                return false;
            }
        }
    }

    /**
     * Returns the hash code value for this map. The hash code of a map is defined to be the sum of the hashCodes of
     * each entry in the map's entrySet view. This ensures that <tt>t1.equals(t2)</tt> implies that <tt>
     * t1.hashCode()==t2.hashCode()</tt> for any two maps <tt>t1</tt> and <tt>t2</tt>, as required by the general
     * contract of Object.hashCode.
     *
     * @return the hash code value for this map.
     *
     * @see    Map.Entry#hashCode()
     * @see    Object#hashCode()
     * @see    Object#equals(Object)
     * @see    #equals(Object)
     */
    public int hashCode()
    {
        // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
        synchronized (cache)
        {
            return cache.hashCode();
        }
    }

    /**
     * Garbage collects the cache, sweeping out any elements that have timed out. This method should really only be
     * invoked in a seperate thread as it does not return (at least not until the {@link #sweepThreadKillFlag} is set).
     *
     * @todo Should really check that the sweep time has actually expired when the thread wakes up.
     */
    private void sweep()
    {
        /*log.fine("private void sweep(): called");*/

        // Loop until the thread is terminated.
        while (true)
        {
            // Take a marked copy of the cache to examine for timed out elements.
            // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
            synchronized (cache)
            {
                /*log.fine("\tMarking " + cache.size() + " objects.");*/

                // Take a copy of everything in the cache into the marked heap.
                marked.putAll(cache);
            }

            // Use synchronized block to own this objects monitor so that it can be waited on.
            // This is needed so that the kill method, and other methods, can wake this thread up.
            synchronized (this)
            {
                // Use a try block as the thread may be woken up during the pause time between sweeps.
                try
                {
                    // Halt the thread between sweeps, configured by the sweepTime property.
                    wait(sweepTime);
                }
                catch (InterruptedException e)
                {
                    // Ignore this, interuption conditions will be tested later.
                }
            }

            // TODO: Should really check that sweepTime has expired.

            // Check the sweep thread kill flag to see if the sweep algorithm has been stopped.
            if (sweepThreadKillFlag)
            {
                return;
            }

            // Create a counter to count the number of elements removed from the cache.
            int i = 0;

            // Create a map to copy the marked heap into. This is done because the following code must iterate
            // over the marked heap whilst modifying it. A copy is used to generate all the keys to iterate over so
            // that the iterator is not disturbed by its underlying data structure being simultaneously modified.
            Map copy = new HashMap();

            // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
            synchronized (cache)
            {
                // Put everything in the marked cache into the copy.
                copy.putAll(marked);
            }

            // Loop over the copy of the marked heap looking for timed out elements.
            for (Object nextKey : copy.keySet())
            {
                // Get the key of the next element from the copy of the marked heap.
                // Extract the current element from the copy of the marked heap as an ElementMonitor object.
                ElementMonitor nextMonitor = (ElementMonitor) copy.get(nextKey);

                // Get the current time in milliseconds.
                long t = System.currentTimeMillis();

                // Check if the current element has not been accessed for a while, configured by the
                // sweepExpiryTime property.
                if ((t - nextMonitor.lastTouched) > sweepExpiryTime)
                {
                    // Synchronize on the cache to ensure its integrity in a multi-threaded environment.
                    synchronized (cache)
                    {
                        // Remove the out of date element from the marked heap and from the cache.
                        marked.remove(nextKey);
                        cache.remove(nextKey);

                        /*log.fine("Element removed from the cache " + nextKey);*/

                        // Increment the count of invalidated elements.
                        i++;

                    }
                }
            }

            /*log.fine(i + " objects removed.");*/
        }
    }

    /**
     * ElementMonitor is a container object for cache elements that pairs the cache element with a timestamp. This
     * timestamp can be upgraded to the present time every time the element is accesses and is used by the sweep
     * algorithm to determine how old elements are.
     */
    private static class ElementMonitor
    {
        /** The cache element. */
        public Object element;

        /** The time it was last touched. */
        public long lastTouched;

        /**
         * Builds an element monitor.
         *
         * @param element   The cache element to store in the monitor.
         * @param timestamp The creation time stamp for the element.
         */
        public ElementMonitor(Object element, long timestamp)
        {
            this.element = element;
            this.lastTouched = timestamp;
        }

        /**
         * Comparison operator. Compares only the element and not the time stamp. This preserves the normal equality
         * between cache elements irrespective of their timestamp.
         *
         * @param  o The object to compare this element monitor to.
         *
         * @return True if the element is equal to the comparators element, false otherwise.
         */
        public boolean equals(Object o)
        {
            // Check that the comparitor is an element monitor and cast it to one and extract its cache element.
            return (o instanceof ElementMonitor) && element.equals(((ElementMonitor) o).element);
        }
    }
}


/*
 * 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 org.hobsoft.microbrowser.tck;

import java.util.List;

import org.hobsoft.microbrowser.Control;
import org.hobsoft.microbrowser.ControlGroup;
import org.hobsoft.microbrowser.ControlNotFoundException;
import org.junit.Test;

import com.squareup.okhttp.mockwebserver.MockResponse;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hobsoft.microbrowser.tck.support.MicrobrowserMatchers.control;
import static org.hobsoft.microbrowser.tck.support.mockwebserver.MockWebServerUtils.url;
import static org.junit.Assert.assertThat;

/**
 * TCK for {@code ControlGroup}.
 */
public abstract class ControlGroupTck extends AbstractMicrobrowserTest
{
	// ----------------------------------------------------------------------------------------------------------------
	// getName tests
	// ----------------------------------------------------------------------------------------------------------------

	@Test
	public void getNameReturnsName()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='text' name='x'/>"
			+ "</form>"
			+ ""));

		String actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("x")
			.getName();

		assertThat("form control group name", actual, is("x"));
	}

	// ----------------------------------------------------------------------------------------------------------------
	// getControls tests
	// ----------------------------------------------------------------------------------------------------------------

	@Test
	public void getControlsReturnsControl()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='text' name='x'/>"
			+ "</form>"
			+ ""));

		List<Control> actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("x")
			.getControls();

		assertThat("form control group control", actual, contains(control("x")));
	}

	@Test
	public void getControlsReturnsControls()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='text' name='x' value='y'/>"
			+ "<input type='text' name='x' value='z'/>"
			+ "</form>"
			+ ""));

		List<Control> actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("x")
			.getControls();

		assertThat("form control group controls", actual, contains(
			control("x", "y"),
			control("x", "z")
		));
	}

	// ----------------------------------------------------------------------------------------------------------------
	// getControl tests
	// ----------------------------------------------------------------------------------------------------------------

	@Test
	public void getControlReturnsControl()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='text' name='c' value='x'/>"
			+ "</form>"
			+ ""));

		Control actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getControl("x");

		assertThat("form control group control", actual, is(control("c", "x")));
	}

	@Test
	public void getControlWhenValuedCheckedCheckboxControlReturnsControl()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x' checked/>"
			+ "</form>"
			+ ""));

		Control actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getControl("x");

		assertThat("form control group control", actual, is(control("c")));
	}

	@Test
	public void getControlWhenValuedUncheckedCheckboxControlReturnsControl()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x'/>"
			+ "</form>"
			+ ""));

		Control actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getControl("x");

		assertThat("form control group control", actual, is(control("c")));
	}

	@Test
	public void getControlWhenValuedCheckedRadioControlReturnsControl()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x' checked/>"
			+ "</form>"
			+ ""));

		Control actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getControl("x");

		assertThat("form control group control", actual, is(control("c")));
	}

	@Test
	public void getControlWhenValuedUncheckedRadioControlReturnsControl()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x'/>"
			+ "</form>"
			+ ""));

		Control actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getControl("x");

		assertThat("form control group control", actual, is(control("c")));
	}

	@Test
	public void getControlWithInvalidValueThrowsException()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='text' name='c' value='x'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");

		thrown().expect(ControlNotFoundException.class);
		thrown().expectMessage("c=y");

		group.getControl("y");
	}

	// ----------------------------------------------------------------------------------------------------------------
	// getValues tests
	// ----------------------------------------------------------------------------------------------------------------

	@Test
	public void getValuesWhenValuedCheckedCheckboxControlReturnsInitialValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x' checked/>"
			+ "<input type='checkbox' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		List<String> actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getValues();

		assertThat("form control group values", actual, contains("x"));
	}

	@Test
	public void getValuesWhenValuedCheckedCheckboxControlsReturnsInitialValues()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x' checked/>"
			+ "<input type='checkbox' name='c' value='y' checked/>"
			+ "</form>"
			+ ""));

		List<String> actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getValues();

		assertThat("form control group values", actual, contains("x", "y"));
	}

	@Test
	public void getValuesWhenValuedUncheckedCheckboxControlReturnsInitialValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x'/>"
			+ "<input type='checkbox' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		List<String> actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getValues();

		assertThat("form control group values", actual, is(empty()));
	}

	@Test
	public void getValuesWhenValuedCheckedRadioControlReturnsInitialValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x' checked/>"
			+ "<input type='radio' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		List<String> actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getValues();

		assertThat("form control group values", actual, contains("x"));
	}

	@Test
	public void getValuesWhenValuedCheckedRadioControlsReturnsInitialValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x' checked/>"
			+ "<input type='radio' name='c' value='y' checked/>"
			+ "</form>"
			+ ""));

		List<String> actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getValues();

		assertThat("form control group values", actual, contains("y"));
	}

	@Test
	public void getValuesWhenValuedUncheckedRadioControlReturnsInitialValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x'/>"
			+ "<input type='radio' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		List<String> actual = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c")
			.getValues();

		assertThat("form control group values", actual, is(empty()));
	}

	// ----------------------------------------------------------------------------------------------------------------
	// setValues tests
	// ----------------------------------------------------------------------------------------------------------------

	@Test
	public void setValuesWhenValuedCheckboxControlsWithCheckedValueSetsValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x'/>"
			+ "<input type='checkbox' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");
		group.setValues("x");

		assertThat("form control group values", group.getValues(), contains("x"));
	}

	@Test
	public void setValuesWhenValuedCheckboxControlsWithCheckedValuesSetsValues()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x'/>"
			+ "<input type='checkbox' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");
		group.setValues("x", "y");

		assertThat("form control group values", group.getValues(), contains("x", "y"));
	}

	@Test
	public void setValuesWhenValuedCheckboxControlsWithUncheckedValueUnsetsValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x' checked/>"
			+ "<input type='checkbox' name='c' value='y' checked/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");
		group.setValues();

		assertThat("form control group values", group.getValues(), is(empty()));
	}

	@Test
	public void setValuesWhenValuedCheckboxControlsWithInvalidValueThrowsException()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x'/>"
			+ "<input type='checkbox' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");

		thrown().expect(IllegalArgumentException.class);
		thrown().expectMessage("Invalid control values: [z]");

		group.setValues("z");
	}

	@Test
	public void setValuesWhenValuedCheckboxControlsWithInvalidValuesThrowsException()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='checkbox' name='c' value='x'/>"
			+ "<input type='checkbox' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");

		thrown().expect(IllegalArgumentException.class);
		thrown().expectMessage("Invalid control values: [p, q]");

		group.setValues("p", "q");
	}

	@Test
	public void setValuesWhenValuedRadioControlsWithCheckedValueSetsValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x'/>"
			+ "<input type='radio' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");
		group.setValues("x");

		assertThat("form control group values", group.getValues(), contains("x"));
	}

	@Test
	public void setValuesWhenValuedRadioControlsWithCheckedValuesSetsValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x'/>"
			+ "<input type='radio' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");
		group.setValues("x", "y");

		assertThat("form control group values", group.getValues(), contains("y"));
	}

	@Test
	public void setValuesWhenValuedRadioControlsWithUncheckedValuePreservesValue()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x' checked/>"
			+ "<input type='radio' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");
		group.setValues();

		assertThat("form control group values", group.getValues(), contains("x"));
	}

	@Test
	public void setValuesWhenValuedRadioControlsWithInvalidValueThrowsException()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x'/>"
			+ "<input type='radio' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");

		thrown().expect(IllegalArgumentException.class);
		thrown().expectMessage("Invalid control values: [z]");

		group.setValues("z");
	}

	@Test
	public void setValuesWhenValuedRadioControlsWithInvalidValuesThrowsException()
	{
		server().enqueue(new MockResponse().setBody(""
			+ "<form name='f'>"
			+ "<input type='radio' name='c' value='x'/>"
			+ "<input type='radio' name='c' value='y'/>"
			+ "</form>"
			+ ""));

		ControlGroup group = newBrowser().get(url(server()))
			.getForm("f")
			.getControlGroup("c");

		thrown().expect(IllegalArgumentException.class);
		thrown().expectMessage("Invalid control values: [p, q]");

		group.setValues("p", "q");
	}
}


/*
 * 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.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import com.thesett.aima.search.QueueBasedSearchMethod;
import com.thesett.aima.search.SearchMethod;
import com.thesett.aima.search.SearchNode;
import com.thesett.aima.search.SearchNotExhaustiveException;
import com.thesett.aima.search.Traversable;
import com.thesett.common.util.SequenceIterator;

/**
 * Searches provides static helper methods to simplify common operations using search methods.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Provide iterator over all solutions to a search space.  {@link QueueBasedSearchMethod}
 *

 *
 * @author Rupert Smith
 */
public class Searches
{
    /**
     * Provides an iterator over a search method, that returns successive search solutions on demand.
     *
     * @param  <T>    The traversable state type of the search.
     * @param  method The search method to iterate over.
     *
     * @return An iterator over all solution states to the search.
     */
    public static <T> Iterator<T> allSolutions(SearchMethod<T> method)
    {
        //return new Filterator<SearchNode<O, T>, T>(allSolutionNodes(method), new ExtractSearchNode<O, T>());

        // Take a final reference to the search method to use from within the inner class.
        final SearchMethod<T> search = method;

        return new SequenceIterator<T>()
            {
                /**
                 * Generates the next element in the search.
                 *
                 * @return The next solution from the search if one is available, or <tt>null</tt> if the search is
                 *         complete.
                 */
                public T nextInSequence()
                {
                    try
                    {
                        return search.search();
                    }
                    catch (SearchNotExhaustiveException e)
                    {
                        // SearchNotExhaustiveException means that the search has completed within its designed parameters
                        // without exhausting the search space. Consequently there are no more solutions to find,
                        // the exception can be ignored and the sequence correctly terminated.
                        e = null;

                        return null;
                    }
                }
            };
    }

    /**
     * Finds the set of all goals of a search.
     *
     * @param  <T>    The traversable state type of the search.
     * @param  method The search method to find all goals of.
     *
     * @return A set of all goals found.
     */
    public static <T> Set<T> setOf(SearchMethod<T> method)
    {
        Set<T> result = new HashSet<T>();
        findAll(result, method);

        return result;
    }

    /**
     * Finds a bag of all goals of a search.
     *
     * @param  <T>    The traversable state type of the search.
     * @param  method The search method to find all goals of.
     *
     * @return A bag of all goals found.
     */
    public static <T> Collection<T> bagOf(SearchMethod<T> method)
    {
        Collection<T> result = new ArrayList<T>();
        findAll(result, method);

        return result;
    }

    /**
     * Provides an iterator over a search method, that returns successive search solutions on demand.
     *
     * @param  <O>    The operator type of the search.
     * @param  <T>    The traversable state type of the search.
     * @param  method The search method to iterate over.
     *
     * @return An iterator over all solutions to the search.
     */
    public static <O, T extends Traversable<O>> Iterator<SearchNode<O, T>> allSolutionPaths(
        QueueBasedSearchMethod<O, T> method)
    {
        // Take a final reference to the search method to use from within the inner class.
        final QueueBasedSearchMethod<O, T> search = method;

        return new SequenceIterator<SearchNode<O, T>>()
            {
                /**
                 * Generates the next element in the search.
                 *
                 * @return The next solution from the search if one is available, or <tt>null</tt> if the search is
                 *         complete.
                 */
                public SearchNode<O, T> nextInSequence()
                {
                    try
                    {
                        return search.findGoalPath();
                    }
                    catch (SearchNotExhaustiveException e)
                    {
                        // SearchNotExhaustiveException means that the search has completed within its designed parameters
                        // without exhausting the search space. Consequently there are no more solutions to find,
                        // the exception can be ignored and the sequence correctly terminated.
                        e = null;

                        return null;
                    }
                }
            };
    }

    /**
     * Finals all solutions to a search and inserts them into the specified collection.
     *
     * @param <T>    The traversable state type of the search.
     * @param result The collection to build up the results in.
     * @param method The search to run.
     */
    private static <T> void findAll(Collection<T> result, SearchMethod<T> method)
    {
        for (Iterator<T> i = allSolutions(method); i.hasNext();)
        {
            T nextSoltn = i.next();

            result.add(nextSoltn);
        }
    }
}


/*
 * 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.logic.fol.wam.debugger.controller;

import java.beans.PropertyChangeEvent;

import com.thesett.aima.logic.fol.wam.debugger.monitor.BreakpointMonitor;
import com.thesett.aima.logic.fol.wam.debugger.monitor.ByteCodeMonitor;
import com.thesett.aima.logic.fol.wam.debugger.text.AttributeSet;
import com.thesett.aima.logic.fol.wam.debugger.text.EnhancedTextGrid;
import com.thesett.aima.logic.fol.wam.debugger.text.EnhancedTextTable;
import com.thesett.aima.logic.fol.wam.debugger.text.TextGridSelectionListener;
import com.thesett.aima.logic.fol.wam.debugger.uifactory.ComponentFactory;
import com.thesett.aima.logic.fol.wam.debugger.uifactory.ControllerLifecycle;
import com.thesett.aima.logic.fol.wam.debugger.uifactory.KeyShortcutMap;
import com.thesett.aima.logic.fol.wam.debugger.uifactory.MainWindow;
import com.thesett.aima.logic.fol.wam.debugger.uifactory.PaneController;
import com.thesett.text.api.TextGridEvent;

/**
 * CodeStepController is the UI controller for the code debugging window. It is responsible for displaying the code
 * being debugged, and the current break-point within it. It also handles all user interaction with the code,
 * principally stepping through it.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Display the code being debugged.

 *
 Display the current break-point.

 *
 Handle user input to step the code.

 *

 *
 * @author Rupert Smith
 */
public class CodeStepController implements ControllerLifecycle
{
    /** Used for debugging. */
    private static final java.util.logging.Logger log =
        java.util.logging.Logger.getLogger(CodeStepController.class.getName());

    /** Holds the component factory used to build the application components. */
    private final ComponentFactory componentFactory;

    /** Holds the main application window frame. */
    private final MainWindow mainWindow;

    /** The text grid model behind the UI component. */
    private EnhancedTextGrid grid;

    /** A text table model that maps down onto the text grid. */
    private EnhancedTextTable table;

    /** Provides the current break-point. */
    private BreakpointMonitor breakpointMonitor;

    /** Provides the current byte code loaded in the target machine. */
    private ByteCodeMonitor byteCodeMonitor;

    /** The pane controller for the UI component displaying the code listing. */
    private PaneController paneController;

    /** The current row corresponding to the instruction currently being stepped. <tt>-1</tt> means no stepped row. */
    private int steppedRow = -1;

    /** The current user selected table row. <tt>-1</tt> means no selected row. */
    private int selectedRow = -1;

    /**
     * Builds the UI controller for the register monitor.
     *
     * @param componentFactory The UI component factory.
     * @param mainWindow       The main window to create the UI component within.
     */
    public CodeStepController(ComponentFactory componentFactory, MainWindow mainWindow)
    {
        this.componentFactory = componentFactory;
        this.mainWindow = mainWindow;
    }

    /**
     * {@inheritDoc}
     *
     *
Creates the register monitor component and hooks it up to the register set.
     */
    public void open()
    {
        // Build a text grid panel in the central position.
        grid = componentFactory.createTextGrid();
        mainWindow.showCentrePane(componentFactory.createTextGridPanel(grid));

        paneController = mainWindow.getCentreController();
        paneController.showVerticalScrollBar();

        // Build a table model on the text grid, to display the code in.
        table = (EnhancedTextTable) grid.createTable(0, 0, 20, 20);

        breakpointMonitor = new BreakpointMonitorImpl();
        byteCodeMonitor = new ByteCodeMonitor(table);

        // Attach listeners for user events on the table.
        grid.addTextGridSelectionListener(new SelectionHandler());

        // Register some keyboard shortcuts to control the code stepping.
        KeyShortcutMap shortcutMap = componentFactory.getKeyShortcutMap();
        mainWindow.setKeyShortcut(shortcutMap.getStep(), "step", new Step());
        mainWindow.setKeyShortcut(shortcutMap.getStepOver(), "step_over", new StepOver());
        mainWindow.setKeyShortcut(shortcutMap.getResume(), "resume", new Resume());
    }

    /** {@inheritDoc} */
    public void close()
    {
    }

    /**
     * Provides access to the underlying break-point monitor.
     *
     * @return The break-point monitor.
     */
    public BreakpointMonitor getBreakpointMonitor()
    {
        return breakpointMonitor;
    }

    /**
     * Provides access to the underlying byte code monitor.
     *
     * @return The byte code monitor.
     */
    public ByteCodeMonitor getByteCodeMonitor()
    {
        return byteCodeMonitor;
    }

    private class Step implements Runnable
    {
        public void run()
        {
            breakpointMonitor.release();
            log.fine("Step");
        }
    }

    private class StepOver implements Runnable
    {
        public void run()
        {
            breakpointMonitor.release();
            log.fine("StepOver");
        }
    }

    private class Resume implements Runnable
    {
        public void run()
        {
            breakpointMonitor.release();
            log.fine("Resume");
        }
    }

    /**
     * Triggers background color highlighting on user row selection.
     */
    private class SelectionHandler implements TextGridSelectionListener
    {
        /** {@inheritDoc} */
        public void select(TextGridEvent e)
        {
            int row = e.getRow();

            if (row != selectedRow)
            {
                log.fine("New mouse selection at : " + e.getColumn() + ", " + row);

                if (row < table.getRowCount())
                {
                    // Clear any previously selected row.
                    if (selectedRow != -1)
                    {
                        // Restore stepping highlight if needed.
                        if (selectedRow == steppedRow)
                        {
                            AttributeSet aset = new AttributeSet();
                            aset.put(AttributeSet.BACKGROUND_COLOR,
                                componentFactory.getColorScheme().getHighlightBackground());
                            grid.insertRowAttribute(aset, selectedRow);
                        }
                        else
                        {
                            grid.insertRowAttribute(null, selectedRow);
                        }
                    }

                    selectedRow = row;

                    AttributeSet aset = new AttributeSet();
                    aset.put(AttributeSet.BACKGROUND_COLOR, componentFactory.getColorScheme().getSelectionBackground());
                    grid.insertRowAttribute(aset, selectedRow);
                }
            }
        }
    }

    /**
     * Triggers background color highlighting on currently stepped row.
     */
    public class BreakpointMonitorImpl extends BreakpointMonitor
    {
        /** The IP register at the current break point. */
        private int ip;

        /** {@inheritDoc} */
        public void propertyChange(PropertyChangeEvent evt)
        {
            String propertyName = evt.getPropertyName();

            if ("IP".equals(propertyName))
            {
                ip = (Integer) evt.getNewValue();

                log.fine("New IP : " + ip);

                highlightSteppedRow(byteCodeMonitor.getRowForAddress(ip));
                scrollToSteppedRow(byteCodeMonitor.getRowForAddress(ip));
            }
        }

        /**
         * Highlights the row that the code execution is currently paused on.
         *
         * @param row The row to highlight.
         */
        public void highlightSteppedRow(int row)
        {
            log.fine("Stepping highlight at : " + row);
            log.fine("table has " + table.getRowCount() + " rows.");

            if (row != steppedRow)
            {
                if (row < table.getRowCount())
                {
                    // Clear any previously selected row.
                    if (steppedRow != -1)
                    {
                        grid.insertRowAttribute(null, steppedRow);

                        // Restore selection highlight if needed.
                        if (selectedRow == steppedRow)
                        {
                            AttributeSet aset = new AttributeSet();
                            aset.put(AttributeSet.BACKGROUND_COLOR,
                                componentFactory.getColorScheme().getSelectionBackground());
                            grid.insertRowAttribute(aset, selectedRow);
                        }
                    }

                    steppedRow = row;

                    AttributeSet aset = new AttributeSet();
                    aset.put(AttributeSet.BACKGROUND_COLOR, componentFactory.getColorScheme().getHighlightBackground());
                    grid.insertRowAttribute(aset, steppedRow);
                }
            }
        }

        /**
         * Adjusts the vertical scroll bar position, if the row that code execution is paused on is too near the start
         * or end of the area of code being displayed.
         *
         * @param row The row to try and bring into reasonable vertical alignment.
         */
        private void scrollToSteppedRow(int row)
        {
            paneController.getVerticalScrollController().scrollTo(row * 12);
        }
    }
}


/*
 * 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;

import java.util.Iterator;

/**
 * A filterator, filters an iterator to produce another iterator by applying a mapping function onto the elements of the
 * source iterator. If the mapping function returns <tt>null</tt> for any element of the source iterator, it is filtered
 * out and not included in the generated iterator. The generated iterator consists of the elements in the source
 * iterator mapped by the mapping function.
 *
 *

CRC Card

 *
ResponsibiltiesCollaborations
 *
Map and filter over an iterator.
 *

 *
 * @author Rupert Smith
 */
public class Filterator<S, T> extends SequenceIterator<T> implements Iterable<T>
{
    /** Holds the source iterator to be filtered. */
    Iterator<S> source;

    /** Holds the mapping and filtering functions. */
    Function<S, T> mapping;

    /**
     * Creates a filterator over the specifed iterator, using the given mapping function.
     *
     * @param source  The source iterator to map and filter.
     * @param mapping The mapping and filtering function.
     */
    public Filterator(Iterator<S> source, Function<S, T> mapping)
    {
        this.source = source;
        this.mapping = mapping;
    }

    /**
     * Generates the next element in the sequence.
     *
     * @return The next element from the sequence if one is available, or <tt>null</tt> if the sequence is complete.
     */
    public T nextInSequence()
    {
        T result = null;

        // Loop until a filtered element is found, or the source iterator is exhausted.
        while (source.hasNext())
        {
            S next = source.next();
            result = mapping.apply(next);

            if (result != null)
            {
                break;
            }
        }

        return result;
    }

    /**
     * Presents this filterator as an iterable.
     *
     * @return This filterator as an iterable.
     */
    public Iterator<T> iterator()
    {
        return this;
    }
}


/*
 * 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.logic.fol.bytecode;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import com.thesett.aima.logic.fol.VariableAndFunctorInterner;
import com.thesett.common.util.doublemaps.SymbolTable;
import com.thesett.common.util.visitor.Acceptor;

/**
 * BaseByteArrayCodeMachine is a basis for implements byte code interpreters that hold their byte code in a byte arry on
 * the Java heap.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Provide a byte buffer and current insertion point for new code.
 *

 *
 * @author Rupert Smith
 */
public abstract class BaseByteArrayCodeMachine<I extends Acceptor, C extends InstructionListing>
    extends BaseCodeMachine
{
    /** Defines the code area size to use for the abstract machine. */
    public static final int CODE_SIZE = 10000;

    /** Holds the byte code. */
    protected byte[] code;

    /** Holds the current load offset within the code area. */
    private int loadPoint;

    /**
     * Creates the base machine.
     *
     * @param symbolTable        The symbol table for the machine.
     * @param interner           The symbol interner for the machine.
     * @param instructionEncoder The binary instruction encoder for the machine.
     */
    public BaseByteArrayCodeMachine(SymbolTable<Integer, String, Object> symbolTable,
        VariableAndFunctorInterner interner, InstructionEncoder instructionEncoder)
    {
        super(symbolTable, interner, instructionEncoder);

        reset();
    }

    /** {@inheritDoc} */
    public ByteBuffer getCodeBuffer(CallPoint callPoint)
    {
        ByteBuffer buffer = ByteBuffer.wrap(code, callPoint.entryPoint, callPoint.length);
        buffer.order(ByteOrder.LITTLE_ENDIAN);

        return buffer;
    }

    /** {@inheritDoc} */
    public int getCodeInsertionPoint()
    {
        return loadPoint;
    }

    /** {@inheritDoc} */
    public void advanceCodeInsertionPoint(int length)
    {
        loadPoint += length;
    }

    /** Resets the machine, clearing out its code area. */
    public void reset()
    {
        code = new byte[CODE_SIZE];
        loadPoint = 0;
    }
}


/*
 * 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.logic.fol.wam.builtins;

import com.thesett.aima.logic.fol.Functor;
import com.thesett.aima.logic.fol.FunctorName;
import com.thesett.aima.logic.fol.wam.compiler.DefaultBuiltIn;
import com.thesett.aima.logic.fol.wam.compiler.WAMInstruction;
import com.thesett.common.util.SizeableLinkedList;

/**
 * Call implements the Prolog 'call' operator. Call resolves its argument as a query. The argument to call may be a
 * variable, but must be bound to a callable functor or atom at the time the call is made in order to be valid.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Call a callable functor or atom as a query for resolution.
 *

 *
 * @author Rupert Smith
 */
public class Call extends BaseBuiltIn
{
    /**
     * Creates a cut built-in to implement the specified functor.
     *
     * @param functor        The functor to implement as a built-in.
     * @param defaultBuiltIn The default built in, for standard compilation and interners and symbol tables.
     */
    public Call(Functor functor, DefaultBuiltIn defaultBuiltIn)
    {
        super(functor, defaultBuiltIn);
    }

    /** {@inheritDoc} */
    public SizeableLinkedList<WAMInstruction> compileBodyArguments(Functor expression, boolean isFirstBody,
        FunctorName clauseName, int bodyNumber)
    {
        // Build the argument to call in the usual way.
        return defaultBuiltIn.compileBodyArguments(expression, isFirstBody, clauseName, bodyNumber);
    }

    /** {@inheritDoc} */
    public SizeableLinkedList<WAMInstruction> compileBodyCall(Functor expression, boolean isFirstBody,
        boolean isLastBody, boolean chainRule, int permVarsRemaining)
    {
        // Used to build up the results in.
        SizeableLinkedList<WAMInstruction> instructions = new SizeableLinkedList<WAMInstruction>();

        // Generate the call or tail-call instructions, followed by the call address, which is f_n of the
        // called program.
        if (isLastBody)
        {
            // Deallocate the stack frame at the end of the clause, but prior to calling the last
            // body predicate.
            // This is not required for chain rules, as they do not need a stack frame.
            if (!chainRule)
            {
                instructions.add(new WAMInstruction(WAMInstruction.WAMInstructionSet.Deallocate));
            }

            instructions.add(new WAMInstruction(WAMInstruction.WAMInstructionSet.CallInternal,
                    (byte) (permVarsRemaining & 0xff), new FunctorName("execute", 1)));
        }
        else
        {
            instructions.add(new WAMInstruction(WAMInstruction.WAMInstructionSet.CallInternal,
                    (byte) (permVarsRemaining & 0xff), new FunctorName("call", 1)));
        }

        return instructions;
    }

    /**
     * Creates a string representation of this functor, mostly used for debugging purposes.
     *
     * @return A string representation of this functor.
     */
    public String toString()
    {
        return "Call: [ arguments = " + toStringArguments() + " ]";
    }
}