class constraint_queue_special
{
    final class constraint_record
    {
        final int var, val;
        final has_propagate cons;
        
        constraint_record(has_propagate cons, int var, int val)
        {
            this.cons=cons;
            this.var=var;
            this.val=val;
        }
        
        public String toString()
        {
            return "constraint_record var: "+var+" val: "+val;
        }
    }
    
    Set<has_make_ac> make_ac_queue;      // queue of subclasses of has_make_ac, to call the make_ac method on.
    
    ArrayList<has_propagate> propagate_queue;   // queue of constraint_record, to call the propagate method on.
    TIntArrayList propagate_queue_var;
    TIntArrayList propagate_queue_val;
    
    Set<constraint_record> special_queue;
    
    // constraints that need to have establish_gac() called.
    ArrayList<has_propagate> toestablish;
    
    constraint_queue()
    {
        make_ac_queue = new HashSet<has_make_ac>();
        
        propagate_queue = new ArrayList<has_propagate>(10000);
        propagate_queue_var = new TIntArrayList(10000);
        propagate_queue_val = new TIntArrayList(10000);
        
        // hack for special
        special_queue = new HashSet<constraint_record>();
        
        toestablish= new ArrayList<has_propagate>(10000);
    }
    
    boolean work_to_do()
    {
        return make_ac_queue.size()>0 || propagate_queue.size()>0 || special_queue.size()>0;
    }
    
    final constraint pop_make_ac_cons()
    {
        Iterator i = make_ac_queue.iterator();
        
        assert i.hasNext();
        
        constraint temp = (constraint) i.next();
        i.remove();
        return temp;
    }
    
    /*final constraint_record pop_prop_cons()
    {
        //int index=propagate_queue.size()-1;
        
        
        
        Iterator i = propagate_queue.iterator();
        
        assert i.hasNext();
        
        constraint_record temp = (constraint_record) i.next();
        i.remove();
        return temp;
    }*/
    
    final constraint_record pop_special_cons()
    {
        Iterator i = special_queue.iterator();
        
        assert i.hasNext();
        
        constraint_record temp = (constraint_record) i.next();
        i.remove();
        return temp;
    }
    
    final void push_constraint(has_make_ac cons)
    {
        make_ac_queue.add(cons);
        //System.out.println("Adding has_make_ac constraint to the queue");
    }
    
    /*final void push_constraint(has_propagate cons, int var, int val)
    {
        propagate_queue.add(new constraint_record(cons, var, val));
        //System.out.println("Adding has_propagate constraint to the queue:"+var+" val:"+val);
    }*/
    
    //hack
    final void push_constraint(has_propagate cons, int var, int val)
    {
        if(false) //cons instanceof gac_schema_predicate_special)    // looks like a bug!
        {
            special_queue.add(new constraint_record(cons, var, val));
        }
        else
        {
            propagate_queue.add(cons);
            propagate_queue_var.add(var);
            propagate_queue_val.add(val);
        }
    }
    
    final boolean process_all()
    {
        constraint_record prop_cons;
        constraint make_ac_cons;
        
        //hacked to do special things first.
        while(work_to_do())
        {
            while(special_queue.size()>0)
            {
                prop_cons=pop_special_cons();
                //System.out.println(prop_cons);
                int temp = ((has_propagate) prop_cons.cons).propagate(prop_cons.var, prop_cons.val);
                if(temp==3)
                {
                    //System.out.println("Individual propagate function returned false.");System.out.println(prop_cons.cons);
                    clearqueue();
                    return false;
                }
                
                // Does not yet deal with throwing constraints away.
            }
            
            // hacked from while to if
            if(make_ac_queue.size()>0)
            {
                make_ac_cons=pop_make_ac_cons();
                //System.out.println(cons.print());
                
                int temp = ((has_make_ac) make_ac_cons).make_ac();
                if(temp==3)
                {
                    clearqueue();
                    return false;
                }
            }
            
            // hacked from while to if
            if(propagate_queue.size()>0)
            {
                //prop_cons=pop_prop_cons();
                
                int index=propagate_queue.size()-1;
                
                int temp = ((has_propagate) propagate_queue.remove(index)).propagate(propagate_queue_var.remove(index), 
                    propagate_queue_val.remove(index));
                if(temp==3)
                {
                    //System.out.println("Individual propagate function returned false.");System.out.println(prop_cons.cons);
                    clearqueue();
                    return false;
                }
                
                // Does not yet deal with throwing constraints away.
            }
        }
        
        return true;
    }
    
    final boolean establish_gac()
    {
        for(has_propagate cons : toestablish)
        {
            if(!cons.establish_gac())
            {
                return false;
            }
        }
        
        return process_all();
    }
    
    void clearqueue()
    {
        make_ac_queue.clear();
        propagate_queue.clear();
        propagate_queue_var.clear();
        propagate_queue_val.clear();
        
        special_queue.clear();
        toestablish.clear();
    }
}


  /*  boolean search()
    {
        // initially currentvar==0
        int currentvar=0;
        
        int assignment[];
        
        boolean cont=true;
        boolean res;
        
        assignment= new int[variables.size()];
        
        assignment[0]=-1; // unassigned
        
        while(cont)
        {
            res=propagate();
            
            if(currentvar==variables.size())
            {                
                if(res==true)
                {
                    // We have found a sol to the csp
                    
                    System.out.print("Solution: ");
                    for(int i=0; i<variables.size(); i++)
                        System.out.print(assignment[i]+" ");
                    System.out.println("");
                    
                    // backtrack to the last universal
                    
                    while(variables.get(currentvar).quant==false && currentvar>=0)
                    {
                        backtrack.backtrack();
                        currentvar--;
                        if(currentvar<0)   // in case we have no universals
                            return true;
                    }
                    
                    res=propagate();
                    // now continue to instantiate that universal again.
                }
                else
                {
                    // no solution -- backtrack to the last existential
                    
                    while(variables.get(currentvar).quant==true && currentvar>=0)
                    {
                        backtrack.backtrack();
                        currentvar--;
                        if(currentvar<0)
                            return false;
                    }
                    
                    res=propagate();
                    // now continue to instantiate that existential again.
                }
            }
            
            if(res=false)
            {
                // backtrack
                backtrack.backtrack();
                currentvar--;
                continue;
            }
            else
            {
                if(variables.get(currentvar).quant)
                {
                    // universal --- try next value
                    assignment[currentvar]++;
                    if(assignment[currentvar]<=variables.get(currentvar).hash.length)
                    {
                        // try next value
                        backtrack.add_backtrack_level();
                        variables.get(currentvar).instantiate(assignment[currentvar]);
                        currentvar++;
                        assignment[currentvar]=-1;  // unassigned
                    }
                    else
                    {
                        // out of values so succeed (backtrack)
                        backtrack.backtrack();
                    }
                }
                else
                {
                    // existential
                    for(int i=0; i<variables.get(currentvar).hash.length; i++)
                    {
                        backtrack.add_backtrack_level();
                        variables.get(currentvar).instantiate(i);
                        boolean result=search(currentvar+1);
                        backtrack.backtrack();
                        if(!result)
                            return false;
                    }
                }
            }
            
            
            res=propagate();
        }
        return true;
    }*/
    
    
    
    
    // search with blocks
/*    boolean search(int currentvar)
    {
        boolean res=propagate();
        
        if(!res)
            return false;
        
        // instantiate currentvar then recursively call.
        
        // Base case
        if(currentvar==variables.length)
        {
            // We have a full assignment with no conflicts.
            return true;
        }
        else
        {// recursive case            
            if(variables[currentvar].quant)
            {
                //universal
                for(int i=0; i<variables[currentvar].hash.length; i++)
                {
                    add_backtrack_level();
                    boolean result=variables[currentvar].instantiate(i);
                    if(!result)
                        System.out.println("Search:universal: this should never happen");
                    result=search(currentvar+1);
                    backtrack();
                    if(!result)
                        return false;
                }
                return true;
            }
            else
            {
                //existential
                for(int i=0; i<variables[currentvar].hash.length; i++)
                {
                    if(variables[currentvar].hash[i]==0)
                    {
                        add_backtrack_level();
                        boolean result=variables[currentvar].instantiate(i);
                        if(!result)
                            System.out.println("search:existential: this should never happen");
                        result=search(currentvar+1);
                        backtrack();
                        if(result)
                            return true;
                    }
                }
                return false;
            }
        }
    }*/
