/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jena.sparql.algebra.optimize;

import org.apache.jena.sparql.algebra.Op ;
import org.apache.jena.sparql.algebra.OpVisitorByTypeBase ;
import org.apache.jena.sparql.algebra.TransformCopy ;
import org.apache.jena.sparql.algebra.Transformer ;
import org.apache.jena.sparql.algebra.op.OpModifier ;
import org.apache.jena.sparql.algebra.op.OpProject ;
import org.apache.jena.sparql.engine.Rename ;

/** Rename variables so that names can be treated globally.
 *  (project) and (group) can hide variables, but we only
 *  need to account for OpProject because group is never
 *  executed linearly. 
 */

public class TransformScopeRename
{
    // Is there an OpProject before any pattern algebra operators?
    // We don't need to rename through this one.

    // Track OpProject

    public static Op transform(Op op)
    {
        return new TransformScopeRename$(op).work() ;
    }

    private static class TransformScopeRename$
    {
        private boolean outerMostOpProject = false ;
        private int projectRenameDepth = 0 ;
        private int projectCount = 0 ;
        
        private Op op ;

        public TransformScopeRename$(Op op)
        {
            this.op = op ;
            {
                Op op2 = op ;
                while( op2 instanceof OpModifier )
                {
                    // If already true ...
                    if ( op2 instanceof OpProject )
                    {
                        outerMostOpProject = true ;
                        break ;
                    }
                    op2 = ((OpModifier)op2).getSubOp() ;
                }
            }
            // Set the project counter: renaming begins when this hits one.
            // Set 2 to there is a project in the top-most OpModifers.
            // This does not cause a rename so start renaming at depth .
            // otherwise rename from depth 1.
            if ( outerMostOpProject )
                projectRenameDepth = 2 ;
            else
                projectRenameDepth = 1;
        }
        
        public Op work()
        {
            return Transformer.transform(new RenameByScope(), op, new BeforeWalk(), new AfterWalk()) ;   
        }


        private class BeforeWalk extends OpVisitorByTypeBase
        {
            @Override
            public void visit(OpProject opProject)
            {
                projectCount++ ;
            }
        }

        private class AfterWalk extends OpVisitorByTypeBase
        {
            @Override
            public void visit(OpProject opProject)
            {
                --projectCount ;
            }
        }

        private class RenameByScope extends TransformCopy
        {
            @Override
            public Op transform(OpProject opProject, Op subOp)
            { 
                // Need to find the right project
                // We already stripped outer modifier. 
                if ( projectCount >= projectRenameDepth )
                    // Inner ones already done.
                    subOp = Rename.renameVars(subOp, opProject.getVars()) ;
                return super.transform(opProject, subOp) ;
            }
        }
    }
}
