// $Id: AbstractFormula.java,v 1.9 2004/02/27 16:30:00 m31 Exp $
//
// This file is part of the program suite "Principia Mathematica II"
// which is a working prototype for the main project:
// "Hilbert II" - http://www.qedeq.org
//
// Copyright 2001-2004  Michael Meyling <mime@qedeq.org>.
//
// "Principia Mathematica II" 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 2 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.

package com.meyling.principia.logic.basic;


import com.meyling.principia.argument.AbstractArgumentList;
import com.meyling.principia.argument.Argument;
import com.meyling.principia.argument.ArgumentException;
import com.meyling.principia.argument.Enumerator;


/**
 * From this abstract implementation of Formula each non abstract Formula
 * must inherit from.
 *
 * @version $Revision: 1.9 $
 * @author  Michael Meyling
 */
public abstract class AbstractFormula extends AbstractArgumentList
        implements Formula {


    /**
     * Constructs an formula.
     *
     * @param  arguments the arguments to make a list of
     * @throws IllegalArgumentException if the argument was a
     *         nullpointer
     */
    public AbstractFormula(final Argument[] arguments) {
        super(arguments);
    }


    /**
     * Replace the every occurence of a {@link SubjectVariable} by a
     * {@link BasicSubjectVariablePatternVariable},
     * every {@link PropositionVariable} by a
     * {@link BasicFormulaPatternVariable}
     * and TODO not yet:
     * every {@link PredicateVariable} by a
     * {@link BasicPredicateVariablePatternVariable}.
     *
     * @param   formula    replace in this formula
     * @return  replaced formula
     * @throws  IllegalArgumentException   if creating replacement failed
     */
    public final static Formula createPattern(final Formula formula)
            throws IllegalArgumentException {

        try {
            // replace all variables by pattern variables, leave only bound
            //  subject variables alone
            final Formula replaced = (Formula) createPattern(formula,
                formula.getFreeSubjectVariables());

            // now, replace all bound subject variables by pattern
            //  variables
            return replaced.replaceBoundSubjectVariables(
                new Enumerator(
                    formula.getSubjectVariables().getHighestNumber() + 1
                ));
        } catch (ArgumentException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }


    /**
     * Replace the every occurence of a {@link SubjectVariable} by a
     * {@link BasicSubjectVariablePatternVariable},
     * every {@link PropositionVariable} by a
     * {@link BasicFormulaPatternVariable} and
     * TODO not any longer: every number of {@link PredicateVariable} by a
     * {@link PatternVariable}.
     *
     * @param   argument    replace in this tree
     * @param   freeVariables   these varables are free in the original
     *              formula
     * @return  replaced argument
     * @throws  ArgumentException   if creating replacement failed
     */
    private final static Argument createPattern(final Argument argument,
        final SubjectVariables freeVariables)
            throws ArgumentException {

        if (argument.getArgumentSize() == 0) {  // this is an atom or an
            return argument;                //  operator without arguments
        } else {
            if (argument instanceof SubjectVariable) {
                // only replace free subject variables
                //  replacement of bound ones must be done
                //  separatly
                if (freeVariables.contains((SubjectVariable) argument)) {
                    return new BasicSubjectVariablePatternVariable(
                        new Argument[] {argument.getArgument(0)});
                } else {    // do nothing with bound variables
                    return argument;
                }
            } else if (argument instanceof PropositionVariable) {
                return new BasicFormulaPatternVariable(
                    new Argument[] {argument.getArgument(0)});
            } else if (argument instanceof PredicateVariable) {
// TODO old code, is BasicPredicateVariablePatternVariable needed?
/*
                return new BasicPredicateVariablePatternVariable(
                    new Argument[] {argument.getArgument(0)});
*/
// TODO old code 2
/*
                return new PredicateVariable(
                    new Argument[] {
                        new PatternVariable(
                            new Argument[] {argument.getArgument(0)}
                        ),
                        createPattern(argument.getArgument(1),
                            freeVariables)
                    });
*/
                return new PredicateVariable(
                    new Argument[] {
                        argument.getArgument(0),
                        createPattern(argument.getArgument(1),
                            freeVariables)
                    });
            } else {
                final Argument[] replaced = new Argument[argument
                    .getArgumentSize()];
                boolean changed = false;
                for (int i = 0; i < argument.getArgumentSize(); i++) {
                    final Argument part = argument.getArgument(i);
                    replaced[i] = createPattern(part, freeVariables);
                    if (!changed && part != replaced[i]) {
                        changed = true;
                    }
                }
                if (changed) {
                    return argument.create(replaced);
                } else {
                    return argument;
                }
            }
        }
    }


    /** @inherited */
    public abstract int getPartFormulaSize();


    /** @inherited */
    public abstract Formula getPartFormula(int i) throws IllegalArgumentException;


    /** @inherited */
    public abstract SubjectVariables getFreeSubjectVariables();


    /** @inherited */
    public abstract SubjectVariables getBoundSubjectVariables();


    /** @inherited */
    public abstract SubjectVariables getSubjectVariables();


    /** @inherited */
    public final Formula replaceBoundSubjectVariable(
            final Enumerator counter,
            final int occurance,
            final SubjectVariable search,
            final SubjectVariable replacement)
            throws ArgumentException {

        if (occurance >= counter.getNumber()) {
            if (this instanceof Quantifier) {
                if (((Quantifier) this).getVariable().equals(search)) {
                    counter.increaseNumber();
                    if (occurance < counter.getNumber()) {
                        return (Formula) replace(search, replacement);
                    }
                }
                // else continue search
            }
            if (getArgumentSize() == 0) {       // this is an atom or an
                return this;                    //  operator without arguments
            } else {
                final Argument[] replaced = new Argument[getArgumentSize()];
                boolean changed = false;
                for (int i = 0; i < getArgumentSize(); i++) {
                    final Argument part = getArgument(i);
                    if (part instanceof Formula) {
                        replaced[i]
                            = ((Formula) part).replaceBoundSubjectVariable(
                            counter, occurance, search, replacement);
                        if (!changed && part != replaced[i]) {
                            changed = true;
                        }
                    } else {
                        replaced[i] = getArgument(i);
                    }
                }
                if (changed) {
                    return (Formula) create(replaced);
                } else {
                    return this;
                }
            }
        } else {    // nothing to do
            return this;
        }
    }


    /** @inherited */
    public final Formula replaceBoundSubjectVariables(
            final Enumerator counter)
            throws ArgumentException {

        if (this instanceof Quantifier) {
            final BasicSubjectVariablePatternVariable variable
                = new BasicSubjectVariablePatternVariable(
                counter.getNumber());
            counter.increaseNumber();
            final Formula subst = (Formula) replace(((Quantifier) this).getVariable(),
                variable);
            final Argument[] replaced = new Argument[getArgumentSize()];
            replaced[0] = variable;
            for (int i = 1; i < subst.getArgumentSize(); i++) {
                final Argument part = subst.getArgument(i);
                if (part instanceof Formula) {
                    replaced[i]
                        = ((Formula) part).replaceBoundSubjectVariables(
                        counter);
                } else {
                    replaced[i] = subst.getArgument(i);
                }
            }
            return (Formula) create(replaced);
        }
        if (getArgumentSize() == 0) {       // this is an atom or an
            return this;                    //  operator without arguments
        } else {
            final Argument[] replaced = new Argument[getArgumentSize()];
            boolean changed = false;
            for (int i = 0; i < getArgumentSize(); i++) {
                final Argument part = getArgument(i);
                if (part instanceof Formula) {
                    replaced[i]
                        = ((Formula) part).replaceBoundSubjectVariables(
                        counter);
                    if (!changed && part != replaced[i]) {
                        changed = true;
                    }
                } else {
                    replaced[i] = getArgument(i);
                }
            }
            if (changed) {
                return (Formula) create(replaced);
            } else {
                return this;
            }
        }
    }


}
