/*
 * ProductionPattern.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.Collections;
using System.Text;

namespace PerCederberg.Grammatica.Parser {

    /**
     * A production pattern. This class represents a set of production
     * alternatives that together forms a single production. A
     * production pattern is identified by an integer id and a name,
     * both provided upon creation. The pattern id is used for
     * referencing the production pattern from production pattern
     * elements.
     *
     * @author   Per Cederberg, <per at percederberg dot net>
     * @version  1.0
     */
    public class ProductionPattern {

        /**
         * The production pattern identity.
         */
        private int id;

        /**
         * The production pattern name.
         */
        private string name;

        /**
         * The syntectic production flag. If this flag is set, the 
         * production identified by this pattern has been artificially 
         * inserted into the grammar.  
         */
        private bool syntetic;

        /**
         * The list of production pattern alternatives.
         */
        private ArrayList alternatives;

        /**
         * The default production pattern alternative. This alternative
         * is used when no other alternatives match. It may be set to 
         * -1, meaning that there is no default (or fallback) alternative.
         */
        private int defaultAlt;

        /**
         * The look-ahead set associated with this pattern.
         */
        private LookAheadSet lookAhead;
        
        /**
         * Creates a new production pattern.
         * 
         * @param id             the production pattern id
         * @param name           the production pattern name
         */
        public ProductionPattern(int id, string name) {
            this.id = id;
            this.name = name;
            this.syntetic = false;
            this.alternatives = new ArrayList();
            this.defaultAlt = -1;
            this.lookAhead = null;
        }

        /**
         * Checks if the syntetic production flag is set. If this flag
         * is set, the production identified by this pattern has been
         * artificially inserted into the grammar. No parse tree nodes
         * will be created for such nodes, instead the child nodes
         * will be added directly to the parent node.
         *   
         * @return true if this production pattern is syntetic, or
         *         false otherwise
         */
        public bool IsSyntetic() {
            return syntetic;
        }

        /**
         * Checks if this pattern is recursive on the left-hand side.
         * This method checks if any of the production pattern
         * alternatives is left-recursive.
         *  
         * @return true if at least one alternative is left recursive, or
         *         false otherwise
         */
        public bool IsLeftRecursive() {
            ProductionPatternAlternative  alt;
            
            for (int i = 0; i < alternatives.Count; i++) {
                alt = (ProductionPatternAlternative) alternatives[i];
                if (alt.IsLeftRecursive()) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * Checks if this pattern is recursive on the right-hand side.
         * This method checks if any of the production pattern
         * alternatives is right-recursive.
         *  
         * @return true if at least one alternative is right recursive, or
         *         false otherwise
         */
        public bool IsRightRecursive() {
            ProductionPatternAlternative  alt;

            for (int i = 0; i < alternatives.Count; i++) {
                alt = (ProductionPatternAlternative) alternatives[i];
                if (alt.IsRightRecursive()) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Checks if this pattern would match an empty stream of
         * tokens. This method checks if any one of the production
         * pattern alternatives would match the empty token stream.
         *  
         * @return true if at least one alternative match no tokens, or
         *         false otherwise
         */
        public bool IsMatchingEmpty() {
            ProductionPatternAlternative  alt;

            for (int i = 0; i < alternatives.Count; i++) {
                alt = (ProductionPatternAlternative) alternatives[i];
                if (alt.IsMatchingEmpty()) {
                    return true;
                }
            }
            return false;
        }

        /**
         * Returns the unique production pattern identity value.
         * 
         * @return the production pattern id
         */
        public int GetId() {
            return id;
        }
    
        /**
         * Returns the production pattern name.
         * 
         * @return the production pattern name
         */
        public string GetName() {
            return name;
        }

        /**
         * Sets the syntetic production pattern flag. If this flag is set, 
         * the production identified by this pattern has been artificially 
         * inserted into the grammar. By default this flag is set to 
         * false.
         * 
         * @param syntetic       the new value of the syntetic flag
         */
        public void SetSyntetic(bool syntetic) {
            this.syntetic = syntetic;
        }

        /**
         * Returns the number of alternatives in this pattern.
         *  
         * @return the number of alternatives in this pattern
         */
        public int GetAlternativeCount() {
            return alternatives.Count;
        }
    
        /**
         * Returns an alternative in this pattern.
         * 
         * @param pos            the alternative position, 0 <= pos < count
         * 
         * @return the alternative found
         */
        public ProductionPatternAlternative GetAlternative(int pos) {
            return (ProductionPatternAlternative) alternatives[pos];
        }

        /**
         * Adds a production pattern alternative.
         * 
         * @param alt            the production pattern alternative to add
         * 
         * @throws ParserCreationException if an identical alternative has
         *             already been added
         */    
        public void AddAlternative(ProductionPatternAlternative alt) {
            if (alternatives.Contains(alt)) {
                throw new ParserCreationException(
                    ParserCreationException.ErrorType.INVALID_PRODUCTION,
                    name,
                    "two identical alternatives exist"); 
            }
            alt.SetPattern(this);
            alternatives.Add(alt);
        }
        
        /**
         * Returns a string representation of this object.
         *
         * @return a token string representation
         */
        public override string ToString() {
            StringBuilder  buffer = new StringBuilder();
            StringBuilder  indent = new StringBuilder();
            int            i;

            buffer.Append(name);
            buffer.Append("(");
            buffer.Append(id);
            buffer.Append(") ");
            for (i = 0; i < buffer.Length; i++) {
                indent.Append(" ");
            }
            for (i = 0; i < alternatives.Count; i++) {
                if (i == 0) {
                    buffer.Append("= ");
                } else {
                    buffer.Append("\n");
                    buffer.Append(indent);
                    buffer.Append("| ");
                }
                buffer.Append(alternatives[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;
        }

        /**
         * Returns the default pattern alternative. The default 
         * alternative is used when no other alternative matches.  
         * 
         * @return the default pattern alternative, or 
         *         null if none has been set
         */
        internal ProductionPatternAlternative GetDefaultAlternative() {
            if (defaultAlt >= 0) {
                object obj = alternatives[defaultAlt];
                return (ProductionPatternAlternative) obj;
            } else {
                return null;
            }
        }
    
        /**
         * Sets the default pattern alternative. The default alternative
         * is used when no other alternative matches.
         *   
         * @param pos            the position of the default alternative
         */
        internal void SetDefaultAlternative(int pos) {
            if (pos >= 0 && pos < alternatives.Count) {
                this.defaultAlt = pos;
            }
        }
    }
}