/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *  
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 * Copyright Tim Hutton and Berlin Brown <berlin dot brown at gmail.com> 2011
 * Copyright Berlin Brown 2012 modifications
 *  
 * Tim Hutton is the original author, but a license not provided in source,
 * GPL was used for similar projects.  If Tim or anyone else has questions, please contact Berlin Brown.
 
 * http://www.sq3.org.uk/Evolution/Squirm3/
 
 * Squirm3 is an artificial chemistry simulation 
 */
package org.berlin.chem.gae;

// SquirmChemistry.java

import java.util.Enumeration;
import java.util.Vector;

import java.util.logging.Logger;

/**
 * Stateful with reactions.
 
 @author bbrown
 *
 */
public class DBSquirmChemistry {

    private static final Logger LOGGER = Logger.getLogger(DBSquirmChemistry.class.getCanonicalName());
    
    private final Vector<DBModelSquirmReaction> reactionsDatabase;

    /**
     * Constructor for squirm chemistry.
     */
    public DBSquirmChemistry(final Vector<DBModelSquirmReaction> db) {
        reactionsDatabase = db;
    }

    public void removeAllReactions() {
        reactionsDatabase.removeAllElements();
    }

    public void addReaction(final DBModelSquirmReaction r) {
        reactionsDatabase.addElement(r);
    }

    public void react(final DBModelSquirmCellSlot cell_grid[][]final DBSquirmCell cell, Vector<DBSquirmCell> neighbours) {
        // try all the reactions in turn
        for (Enumeration<DBModelSquirmReaction> e = reactionsDatabase.elements(); e.hasMoreElements();) {
            tryReaction(cell_grid, cell, neighbours, (DBModelSquirmReactione.nextElement());
        // End of the for //
    }

    protected void tryReaction(final DBModelSquirmCellSlot cell_grid[][], DBSquirmCell cell, Vector<DBSquirmCell> neighbours, final DBModelSquirmReaction r) {
        tryReaction(cell_grid, cell, neighbours, r.us_type, r.us_state, r.current_bond, r.them_type, r.them_state,
                r.future_us_state, r.future_bond, r.future_them_state);
    }

    protected void tryReaction(final DBModelSquirmCellSlot cell_grid[][]final DBSquirmCell cell, final Vector<DBSquirmCell> neighbours,
            char us_type, int us_state, boolean current_bond, char them_type, int them_state, int future_us_state,
            boolean future_bond, int future_them_state) {

        // us_type is one of {e,f,a,b,c,d,x}
        if (us_type != 'e' && us_type != 'f' && us_type != 'a' && us_type != 'b' && us_type != 'c' && us_type != 'd'
                && us_type != 'x')
            throw new Error("SquirmChemistry::Reaction() : invalid us_type");

        // them_type is one of {e,f,a,b,c,d,x,y}
        if (them_type != 'e' && them_type != 'f' && them_type != 'a' && them_type != 'b' && them_type != 'c'
                && them_type != 'd' && them_type != 'x' && them_type != 'y')
            throw new Error("SquirmChemistry::Reaction() : invalid them_type");

        // sanity check on the states requested
        if (us_state < || them_state < || future_us_state < || future_them_state < 0)
            throw new Error("SquirmChemistry::tryReaction() : states less than zero not permitted");

        // are we the right kind of cell for this reaction?
        if ((us_type != 'x' && cell.isTypeAndState(us_type, us_state)) || (us_type == 'x' && cell.isState(us_state))) {
            // do we have a neighbour (bonded/not) that is the right kind for
            // this reaction?
            Vector<DBSquirmCell> search_from = current_bond ? cell.getBonds() : neighbours;
            Vector<DBSquirmCell> ns;
            // if them_type specified then search for it
            if (them_type != 'x' && them_type != 'y')
                ns = getThoseOfTypeAndState(search_from, them_type, them_state);
            // if unspecified but to be same as us_type then search for it
            else if (them_type == 'x' && us_type == 'x')
                ns = getThoseOfTypeAndState(search_from, cell.getType(), them_state);
            // must be unspecified
            else if ((them_type == 'x' && us_type != 'x'|| them_type == 'y')
                ns = getThoseOfState(search_from, them_state);
            else
                throw new Error("SquirmChemistry::tryReaction() : unexpected case statement");
            // try the reaction on each of the possibles
            for (Enumeration<DBSquirmCell> e = ns.elements(); e.hasMoreElements();) {
                DBSquirmCell n = (DBSquirmCelle.nextElement();
                // reactions can happen if the two cells are right next to each
                // other (share a face) or over a diagonal (share a corner) if
                // the other diagonal
                // doesn't have a bond
                boolean can_react = false;
                if (rightNextToEachOther(cell, n)) {
                    can_react = true;
                else {
                    // if either other diagonal square is empty then OK
                    if (cell_grid[cell.getX()][n.getY()].queryEmpty() || cell_grid[n.getX()][cell.getY()].queryEmpty())
                        can_react = true;
                    else {
                        // otherwise, if there is no bond between diagonals then
                        // still OK
                        DBSquirmCell cellA = cell_grid[cell.getX()][n.getY()].getOccupant();
                        DBSquirmCell cellB = cell_grid[n.getX()][cell.getY()].getOccupant();
                        if (!cellA.getBonds().contains(cellB))
                            can_react = true;
                    }
                // End of the if - else //
                if (can_react) {
                    // make or break bonds as specified
                    if (current_bond && !future_bond) {
                        LOGGER.info("Breaking bond : UsType=" + us_type + " UsState=" + us_state + " ThemType=" + them_type + " ThemState=" + them_state);
                        cell.breakBondWith(n);
                    else if (!current_bond && future_bond) {
                        LOGGER.info("Making bond : UsType=" + us_type + " UsState=" + us_state + " ThemType=" + them_type + " ThemState=" + them_state);                                               
                        cell.makeBondWith(n);
                    // End of the if //
                    // set our states to their new values
                    cell.setState(future_us_state);
                    n.setState(future_them_state);
                    break;
                // End of the if //
            }
        }
    // End of the method //

    protected Vector<DBSquirmCell> getThoseOfTypeAndState(Vector<DBSquirmCell> cells, char type, int state) {
        return getThoseOfTypeAndState(cells, DBModelSquirmCellProperties.getType(type), state);
    }

    protected Vector<DBSquirmCell> getThoseOfTypeAndState(Vector<DBSquirmCell> cells, int type, int state) {
        // do any of these cells match the type and state specified?
        // if so return all that match (empty list if none)
        final Vector<DBSquirmCell> v = new Vector<DBSquirmCell>();
        DBSquirmCell c;
        for (Enumeration<DBSquirmCell> enumx = cells.elements(); enumx.hasMoreElements();) {
            c = ((DBSquirmCellenumx.nextElement());
            if (type != -1) {
                if (c.isTypeAndState(type, state))
                    v.addElement(c);
            else if (c.isState(state))
                v.addElement(c);
        }

        return v;
    }

    protected Vector<DBSquirmCell> getThoseOfState(final Vector<DBSquirmCell> cells, int state) {
        // do any of these cells match the state specified? (any type)
        // if so return all that match (empty list if none)
        final Vector<DBSquirmCell> v = new Vector<DBSquirmCell>();
        DBSquirmCell c;
        for (Enumeration<DBSquirmCell> enumx = cells.elements(); enumx.hasMoreElements();) {
            c = ((DBSquirmCellenumx.nextElement());
            if (c.isState(state)) {
                v.addElement(c);
            }
        }
        return v;
    }

    protected boolean rightNextToEachOther(final DBSquirmCell cell1, final DBSquirmCell cell2) {
        return (Math.abs(cell1.getX() - cell2.getX()) + Math.abs(cell1.getY() - cell2.getY()) 2);
    }

// End of the class //