/*
 * Copyright (c) 2009 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 *
 */

package com.jscape.util.functor;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;

/**
 * @author Alex Usun
 */
public abstract class Predicate
        implements Serializable {

    public static final Predicate TRUE = new Predicate() {
        public boolean evaluate() {
            return true;
        }
    };

    public static final Predicate FALSE = new Predicate() {
        public boolean evaluate() {
            return false;
        }
    };

    /**
     * @param base base
     *
     * @return Predicate instance
     */
    public static Predicate not(Predicate base) {
        return new Not(base);
    }

    /**
     * @return boolean value
     */
    public abstract boolean evaluate();

    /**
     * @param predicates predicates
     *
     * @return Predicate instance
     */
    public Predicate and(Predicate... predicates) {
        Predicate predicate = this;

        for (Predicate next : predicates) {
            predicate = new And(predicate, next);
        }

        return predicate;
    }

    /**
     * @param predicates predicates
     *
     * @return Predicate instance
     */
    public Predicate or(Predicate... predicates) {
        Predicate predicate = this;

        for (Predicate next : predicates) {
            predicate = new Or(predicate, next);
        }

        return predicate;
    }

    public static class And
            extends Predicate {

        private List<Predicate> predicates;

        /**
         * @param first  first
         * @param second second
         */
        public And(Predicate first, Predicate second) {
            this.predicates = new LinkedList<>();

            add(first);
            add(second);
        }

        /**
         * @return boolean value
         */
        public boolean evaluate() {
            for (Predicate predicate : this.predicates) {
                if (!predicate.evaluate()) {
                    return false;
                }
            }

            return true;
        }

        /**
         * @param predicate predicate
         */
        private void add(Predicate predicate) {
            if (predicate instanceof And) {
                this.predicates.addAll(((And) predicate).predicates);
            } else {
                this.predicates.add(predicate);
            }
        }

    }

    public static class Or
            extends Predicate {

        private List<Predicate> predicates;

        /**
         * @param first  first
         * @param second second
         */
        public Or(Predicate first, Predicate second) {
            this.predicates = new LinkedList<>();

            add(first);
            add(second);
        }

        /**
         * @return boolean value
         */
        public boolean evaluate() {
            for (Predicate predicate : this.predicates) {
                if (predicate.evaluate()) {
                    return true;
                }
            }

            return false;
        }

        /**
         * @param predicate predicate
         */
        private void add(Predicate predicate) {
            if (predicate instanceof Or) {
                this.predicates.addAll(((Or) predicate).predicates);
            } else {
                this.predicates.add(predicate);
            }
        }

    }

    public static class Not
            extends Predicate {

        private Predicate base;

        /**
         * @param base base
         */
        public Not(Predicate base) {
            this.base = base;
        }

        /**
         * @return boolean value
         */
        public boolean evaluate() {
            return !this.base.evaluate();
        }
    }


}
