/*
 * ProductionPatternAlternative.cs
 *
 * This work 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 work 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, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 * As a special exception, the copyright holders of this library give
 * you permission to link this library with independent modules to
 * produce an executable, regardless of the license terms of these
 * independent modules, and to copy and distribute the resulting
 * executable under terms of your choice, provided that you also meet,
 * for each linked independent module, the terms and conditions of the
 * license of that module. An independent module is a module which is
 * not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the
 * library, but you are not obligated to do so. If you do not wish to
 * do so, delete this exception statement from your version.
 *
 * Copyright (c) 2003 Per Cederberg. All rights reserved.
 */

using System;
using System.Collections;
using System.Text;

namespace PerCederberg.Grammatica.Parser {

    /**
     * A production pattern alternative. This class represents a list of 
     * production pattern elements. In order to provide productions that
     * cannot be represented with the element occurance counters, multiple 
     * alternatives must be created and added to the same production 
     * pattern. A production pattern alternative is always contained 
     * within a production pattern.  
     *
     * @author   Per Cederberg, <per at percederberg dot net>
     * @version  1.0
     */
    public class ProductionPatternAlternative {

        /**
         * The production pattern.
         */
        private ProductionPattern pattern;

        /**
         * The element list.
         */
        private ArrayList elements = new ArrayList();
        
        /**
         * The look-ahead set associated with this alternative.
         */
        private LookAheadSet lookAhead = null;

        /**
         * Creates a new production pattern alternative.  
         */
        public ProductionPatternAlternative() {
        }

        /**
         * Checks if this alternative is recursive on the left-hand
         * side. This method checks all the possible left side
         * elements and returns true if the pattern itself is among
         * them.
         *  
         * @return true if the alternative is left side recursive, or
         *         false otherwise
         */
        public bool IsLeftRecursive() {
            ProductionPatternElement  elem;
            
            for (int i = 0; i < elements.Count; i++) {
                elem = (ProductionPatternElement) elements[i];
                if (elem.GetId() == pattern.GetId()) {
                    return true;
                } else if (elem.GetMinCount() > 0) {
                    break;
                }
            }
            return false;
        }
    
        /**
         * Checks if this alternative is recursive on the right-hand side. 
         * This method checks all the possible right side elements and 
         * returns true if the pattern itself is among them.
         *  
         * @return true if the alternative is right side recursive, or
         *         false otherwise
         */
        public bool IsRightRecursive() {
            ProductionPatternElement  elem;
            
            for (int i = elements.Count -1; i >= 0; i--) {
                elem = (ProductionPatternElement) elements[i];
                if (elem.GetId() == pattern.GetId()) {
                    return true;
                } else if (elem.GetMinCount() > 0) {
                    break;
                }
            }
            return false;
        }

        /**
         * Checks if this alternative would match an empty stream of 
         * tokens. This check is equivalent of getMinElementCount() 
         * returning zero (0).
         *  
         * @return true if the rule can match an empty token stream, or
         *         false otherwise
         */
        public bool IsMatchingEmpty() {
            return GetMinElementCount() == 0;
        }

        /**
         * Returns the production pattern containing this alternative.
         * 
         * @return the production pattern for this alternative
         */
        public ProductionPattern GetPattern() {
            return pattern;
        }

        /**
         * Changes the production pattern containing this alternative.
         * This method should only be called by the production pattern
         * class.
         * 
         * @param pattern        the new production pattern
         */
        internal void SetPattern(ProductionPattern pattern) {
            this.pattern = pattern;
        }

        /**
         * Returns the number of elements in this alternative.
         *  
         * @return the number of elements in this alternative
         */
        public int GetElementCount() {
            return elements.Count;
        }

        /**
         * Returns the minimum number of elements needed to satisfy
         * this alternative. The value returned is the sum of all the
         * elements minimum count.
         * 
         * @return the minimum number of elements
         */
        public int GetMinElementCount() {
            ProductionPatternElement  elem;
            int                       min = 0;
            
            for (int i = 0; i < elements.Count; i++) {
                elem = (ProductionPatternElement) elements[i];
                min += elem.GetMinCount();
            }
            return min;
        }
    
        /**
         * Returns the maximum number of elements needed to satisfy
         * this alternative. The value returned is the sum of all the
         * elements maximum count.
         * 
         * @return the maximum number of elements
         */
        public int GetMaxElementCount() {
            ProductionPatternElement  elem;
            int                       max = 0;
        
            for (int i = 0; i < elements.Count; i++) {
                elem = (ProductionPatternElement) elements[i];
                if (elem.GetMaxCount() >= Int32.MaxValue) {
                    return Int32.MaxValue;
                } else {
                    max += elem.GetMaxCount();
                }
            }
            return max;
        }

        /**
         * Returns an element in this alternative.
         * 
         * @param pos            the element position, 0 <= pos < count
         * 
         * @return the element found
         */
        public ProductionPatternElement GetElement(int pos) {
            return (ProductionPatternElement) elements[pos];
        }

        /**
         * Adds a token to this alternative. The token is appended to
         * the end of the element list. The multiplicity values
         * specified define if the token is optional or required, and
         * if it can be repeated.
         * 
         * @param id             the token (pattern) id
         * @param min            the minimum number of occurancies
         * @param max            the maximum number of occurancies, or
         *                       -1 for infinite 
         */
        public void AddToken(int id, int min, int max) {
            AddElement(new ProductionPatternElement(true, id, min, max));
        }
    
        /**
         * Adds a production to this alternative. The production is
         * appended to the end of the element list. The multiplicity
         * values specified define if the production is optional or
         * required, and if it can be repeated.
         * 
         * @param id             the production (pattern) id
         * @param min            the minimum number of occurancies
         * @param max            the maximum number of occurancies, or
         *                       -1 for infinite 
         */
        public void AddProduction(int id, int min, int max) {
            AddElement(new ProductionPatternElement(false, id, min, max));
        }

        /**
         * Adds a production pattern element to this alternative. The
         * element is appended to the end of the element list.
         * 
         * @param elem           the production pattern element
         */
        public void AddElement(ProductionPatternElement elem) {
            elements.Add(elem);
        }

        /**
         * Adds a production pattern element to this alternative. The
         * multiplicity values in the element will be overridden with
         * the specified values. The element is appended to the end of
         * the element list.
         * 
         * @param elem           the production pattern element
         * @param min            the minimum number of occurancies
         * @param max            the maximum number of occurancies, or
         *                       -1 for infinite 
         */
        public void AddElement(ProductionPatternElement elem, 
                               int min, 
                               int max) {

            if (elem.IsToken()) {
                AddToken(elem.GetId(), min, max);
            } else {
                AddProduction(elem.GetId(), min, max);
            }
        }

        /**
         * Checks if this object is equal to another. This method only
         * returns true for another production pattern alternative
         * with identical elements in the same order.
         * 
         * @param obj            the object to compare with
         * 
         * @return true if the object is identical to this one, or
         *         false otherwise
         */
        public override bool Equals(object obj) {
            ProductionPatternAlternative  alt;
            
            if (obj is ProductionPatternAlternative) {
                return Equals((ProductionPatternAlternative) obj);
            } else {
                return false;
            }
        }

        /**
         * Checks if this alternative is equal to another. This method 
         * returns true if the other production pattern alternative 
         * has identical elements in the same order.
         * 
         * @param alt            the alternative to compare with
         * 
         * @return true if the object is identical to this one, or
         *         false otherwise
         */
        public bool Equals(ProductionPatternAlternative alt) {
            if (elements.Count != alt.elements.Count) {
                return false;
            }
            for (int i = 0; i < elements.Count; i++) {
                if (!elements[i].Equals(alt.elements[i])) {
                    return false;
                }
            }
            return true;
        }

        /**
         * Returns a string representation of this object.
         *
         * @return a token string representation
         */
        public override string ToString() {
            StringBuilder  buffer = new StringBuilder();
            
            for (int i = 0; i < elements.Count; i++) {
                if (i > 0) {
                    buffer.Append(" ");
                }
                buffer.Append(elements[i]);
            }
            return buffer.ToString();
        }
    
        /**
         * Returns the look-ahead set associated with this alternative.
         * 
         * @return the look-ahead set associated with this alternative
         */
        internal LookAheadSet GetLookAhead() {
            return lookAhead;
        }
    
        /**
         * Sets the look-ahead set for this alternative.
         * 
         * @param lookAhead      the new look-ahead set
         */
        internal void SetLookAhead(LookAheadSet lookAhead) {
            this.lookAhead = lookAhead;
        }
    }
}