package DOPParser;
/*
 * Rule.java
 *
 * Created on 24 november 2005, 21:24
 *
 */

/**
 *
 * @author gideon
 */
import java.util.*;


public class Rule implements Cloneable {
    //arraylist of gewone array??? Arraylist hoef je lengte niet te declareren
    private ArrayList<Constituent> rightHandSide;
    private NonTerminal leftHandSide;
    //xxx countOfUse only if you donot use Goodman
    private int countOfUse = 1;
    private double ruleProbability;
            
  
    /**
     *  Creates a new instance of Rule of the form X -> a
     *  @param cat1 LeftHandSide of the rule.
     */
   
    public Rule(NonTerminal cat1, Constituent myConstituent) {
        //new ArrayList<String> Rule;
        this.leftHandSide = cat1;
        this.rightHandSide = new ArrayList<Constituent>(1);
        this.rightHandSide.add(myConstituent);
        
    }
    
    /**
     *  Creates a new instance of Rule of the form X -> YZ
     *  @param catX LeftHandSide of the rule.
     *  @param catY RightHandSide of the rule.
     *  @param catZ RightHandSide of the rule.
     */
    public Rule(NonTerminal catX, NonTerminal catY, NonTerminal catZ) {
        this.leftHandSide = catX;
        this.rightHandSide = new ArrayList<Constituent>(2);
        this.rightHandSide.add(catY);
        this.rightHandSide.add(catZ);
        
    }
    
    //X -> ArrayList van Constituents (vrij, voor artificial grammar)
    public Rule(NonTerminal cat1, ArrayList<Constituent> constituentArray) {
        this.leftHandSide = cat1;
        this.rightHandSide = constituentArray;
    }
    
    public ArrayList<Constituent> getRightHandSide(){
        return this.rightHandSide;
    }
    
    public NonTerminal getLeftHandSide(){
        return this.leftHandSide;
    }
    
    public boolean containsConstituent(Constituent myConstituent){
        //find constituent in lhs and rhs
        if (myConstituent.equals(this.leftHandSide)) {
            return true;
        }
        else{
            //iterate over rhs
            for (Constituent rhsConstituent : this.rightHandSide) {
                if (myConstituent.equals(rhsConstituent))  return true;
            }
        }
        return false;
    }
   
    /*
    public void computeRuleProbability_NotGoodman(NonTerminal associatedNonTerminal){
        
        // OLD VERSION
        int totalCount = 0;
        for (Rule myRule : associatedNonTerminal.getRules()) {
            totalCount += myRule.getCount();
        }
        this.ruleProbability = ((double) this.countOfUse)/((double) totalCount);
        //return (double) this.countOfUse/totalCount;
        
    }
    */
        public void computeRuleProbability(){
        //2 nonterminal children: prob = subtrees_child1*subtrees_child2/subtrees_LHS
        //preterminal rule:??? als Aj dan is p=1, als A dan p=1/a???
        double myProb = 1d;
        int nrSubtrees1, nrSubtrees2;
        NonTerminal nonTerminalchild1, nonTerminalchild2;
        
        if( this.getRightHandSide().size()==2)  {
            //???? als Bj en/of Cj preterminals zijn hebben ze beide 0 subtrees!!!
            nonTerminalchild1 = (NonTerminal) this.getRightHandSide().get(0);
            nonTerminalchild2 = (NonTerminal) this.getRightHandSide().get(1);
            //find out exterior or interior
            if (nonTerminalchild1.getName().contains("@")) nrSubtrees1 = nonTerminalchild1.getnrSubtrees();
            else nrSubtrees1 = 1;
            if (nonTerminalchild2.getName().contains("@")) nrSubtrees2 = nonTerminalchild2.getnrSubtrees();
            else nrSubtrees2 = 1;
            myProb = ((double) nrSubtrees1)*((double) nrSubtrees2)/((double) this.leftHandSide.getnrSubtrees());
        }
        else {
            //either TOP, TOP@1 etc or preterminal
            //TOP can also have more than one child, then it is included in the first if
            if (this.leftHandSide.getName().contains("TOP")) {  //TOP   ???
                nonTerminalchild1 = (NonTerminal) this.getRightHandSide().get(0);
                //find out exterior or interior
                if (nonTerminalchild1.getName().contains("@")) nrSubtrees1 = nonTerminalchild1.getnrSubtrees();
                else nrSubtrees1 = 1;
                myProb = ((double) nrSubtrees1)/((double) this.leftHandSide.getnrSubtrees());  
                
            }
            else { //PRETERMINAL
                //??? I assume this: if Aj is interior, then p=1, if A exterior and subtrees(A)>0, then p=1/a, otherwise 1 ??? XXX
                //NO 
                //if (this.leftHandSide.getName().contains("@")) 
                //    myProb = 1d;
                //else {
                   // if (this.leftHandSide.getnrSubtrees()>0) 
                        myProb = 1d/((double) this.leftHandSide.getnrSubtrees());   //there are always >0
                  //  else myProb = 1d;
                //}
            }
        }
        this.ruleProbability = myProb;
        //return myProb;
        
    }
    
    public double getRuleProbability() {
        return this.ruleProbability;
    }
    
    public void setRuleProbability(double myRuleProb) {
        this.ruleProbability = myRuleProb;
    }
    
    public boolean equals(Object obj){
        if(!(obj instanceof Rule)){
            return false;
        }
        
        Rule other = (Rule) obj;
        if(!(other.leftHandSide.getName().equals(this.leftHandSide.getName()))){
            return false;
        }
        //compare rhs length, if equal, then compare all the constituents of the rhs
        if(!(other.rightHandSide.size() ==  this.rightHandSide.size())){
            return false;
        }
        else
        {
            //return other.rightHandSide.equals(this.rightHandSide); : not sure this works
            //better turn everything into strings
            for (int i = 0; i< this.rightHandSide.size(); i++) {
                if (this.rightHandSide.get(i)==null || other.rightHandSide.get(i)==null) {
                    System.out.println("XXXXXXXXXXXXXXXXXXXXXX BUG!!!!! i="+ i + "; size=" + other.rightHandSide.size());
                    System.out.println("other.leftHandSide.getName()=" + other.leftHandSide.getName());
                    System.out.println("this.rightHandSide(i)=" + this.rightHandSide.get(i).getName());
                    //System.out.println("other.rightHandSide(i)=" + other.rightHandSide.get(i).getName());
                    return false;
                }
                
                if (!(this.rightHandSide.get(i).getName().equals(other.rightHandSide.get(i).getName()))) return false;
            }
            return true;
        }
    }
    
    public Object clone() {
            try {
                Rule aobj = (Rule) super.clone();
                aobj.rightHandSide = (ArrayList<Constituent>) rightHandSide.clone();
                aobj.leftHandSide = (NonTerminal) leftHandSide.clone();
                aobj.countOfUse = this.countOfUse;
                aobj.ruleProbability = this.ruleProbability;
                return aobj;
            }
            catch (CloneNotSupportedException e) {
                throw new InternalError(e.toString());
            }
        }

    public int hashCode(){
        return this.rightHandSide.hashCode();
    } 
    
    /**
     * returns count of RHS symbols 
     */
    public int countSymbolsinRHS(){
        return this.rightHandSide.size() ;
    }
    
    public int getCount(){
        return this.countOfUse;

    }
    
     public void increaseCount(){
         this.countOfUse++;
    }
     
    public void increaseCount(int step){
        this.countOfUse += step;
    }
    
    
    public String toString(){
        StringBuffer result = new StringBuffer();
        
        result.append(leftHandSide.getName()).append(" -->");
        
        for(Constituent con : rightHandSide){
            result.append(' ').append(con.getName());
        }
        
        return result.toString();
    }
}
