/**
 * @(#)TranslationsCounter.java
 * @author JLMJ
 * @date 2001-10-04
 */
package minoids1.counter;

import minoids1.Box;
import minoids1.Bits;
import java.util.Vector;

final public class TranslationsCounter
{
    private Box box;
    private int minOrder;
    private int maxOrder;
    private Bits[] boundingWallsCheckersBits;
    private Bits[] connectionsBits;
    private int translations;
    private boolean storeMinoids;
    private Vector vector;
    
    public TranslationsCounter(Box box, boolean storeMinoids) {
        this.box = box;
        this.storeMinoids = storeMinoids;
        this.minOrder = box.getMinOrder();
        this.maxOrder = box.getMaxOrder();
        this.boundingWallsCheckersBits = box.getBoundingWallsCheckersBits();
    }
    
    public Box getBox() {
        return box;
    }
    
    public Bits[] getMinoidsInBox() {
        if (vector == null)
            return null;
        Bits[] bits = new Bits[vector.size()];
        for (int i=0; i < vector.size(); i++)
            bits[i] = (Bits)vector.elementAt(i);
        return bits;
    }
    
    public void countAnyMinoidsTranslations(int order) {
        connectionsBits = box.getAnyConnectionsBits();
        countTranslations(order);
    }
    
    public void countHardMinoidsTranslations(int order) {
        connectionsBits = box.getHardConnectionsBits();
        countTranslations(order);
    }

    public void countSoftMinoidsTranslations(int order) {
        connectionsBits = box.getSoftConnectionsBits();
        countTranslations(order);
    }
    
    private void countTranslations(int order) {
        if ((order < 1) || (order < minOrder) || (order > maxOrder))
            return;
        translations = 0;
        if (storeMinoids)
            vector = new Vector();
        Bits number = new Bits(maxOrder, order);
        recursiveCounting(number, maxOrder, order - 1);
    }
    
    public int getTranslations() {
        return translations;
    }
    
    private boolean allWallsReached;
    
    private void recursiveCounting(Bits number, int bits, int ones) {
        // If at least one check fails do not count
        allWallsReached = true;
        for (int j=0; j < boundingWallsCheckersBits.length; j++)
            if (boundingWallsCheckersBits[j].isClearAfterAndWith(number))
                allWallsReached = false;
        
        if (allWallsReached && isConnected(number)) {
            translations++;
            if (storeMinoids)
                vector.addElement(number);
        }
        
        if (ones < 0)
            return;
        Bits mask = new Bits(maxOrder, bits);
        mask.doNot(); // set up a upper mask;
        mask.doOrWith(new Bits(maxOrder, ones)); // set up a lower mask
        mask.doAndWith(number); // put the number inside the window
        
        Bits window = new Bits(maxOrder, 0);
        window.setBit(ones);
        for (int j=1; j < (bits - ones); j++) {
            window.shiftLeft(false);
            Bits nextNumber = new Bits(mask);
            nextNumber.doOrWith(window);
            recursiveCounting(nextNumber, ones + j, ones - 1);
        }
    }
    
    private Bits visitedBits = new Bits();
    
    private boolean isConnected(Bits number) {
        if (number.isClear())
            return false;
        visitedBits.set(maxOrder, 0);
        Bits rotor = new Bits(maxOrder, 1);
        int pos = 0;
        while (rotor.isClearAfterAndWith(number)) {
            rotor.shiftLeft(false);
            pos++;
        }
        // check the seed as visited
        visitedBits.doOrWith(rotor);
        recursiveConnected(pos, number);
        return (visitedBits.equals(number));
    }
    
    private void recursiveConnected(int p, final Bits number) {
        Bits mask = new Bits(connectionsBits[p]);
        Bits rotor = new Bits(maxOrder, 1);
        for (int pos=0; pos < maxOrder; pos++, rotor.shiftLeft(false)) {
            if (rotor.isNotClearAfterAndWith(mask)) {
                if (rotor.isNotClearAfterAndWith(visitedBits)) {
                    continue; // avoids closed loops
                } else if (rotor.isNotClearAfterAndWith(number)) {
                    visitedBits.doOrWith(rotor);
                    recursiveConnected(pos, number);
                }
            }
        }
    }
}
