/*
 * 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.sdb.core.sqlexpr;

import org.apache.jena.atlas.io.IndentedWriter ;
import org.apache.jena.sdb.sql.SQLUtils ;
import org.apache.jena.sdb.util.RegexUtils ;

public class SqlExprGenerateSQL implements SqlExprVisitor
{
    /* Precedence (from MySQL)
     * :=
||, OR, XOR
&&, AND
NOT
BETWEEN, CASE, WHEN, THEN, ELSE
=, <=>, >=, >, <=, <, <>, !=, IS, LIKE, REGEXP, IN
|
&
<<, >>
-, +
*, /, DIV, %, MOD
^
- (unary minus), ~ (unary bit inversion)
!
BINARY, COLLATE
     */
    /* Decreasing : PostgreSQL
Operator/Element    Associativity   Description
.   left    table/column name separator
::  left    PostgreSQL-style typecast
[ ] left    array element selection
-   right   unary minus
^   left    exponentiation
* / %   left    multiplication, division, modulo
+ - left    addition, subtraction
IS      IS TRUE, IS FALSE, IS UNKNOWN, IS NULL
ISNULL      test for null
NOTNULL     test for not null
(any other) left    all other native and user-defined operators
IN      set membership
BETWEEN     range containment
OVERLAPS        time interval overlap
LIKE ILIKE SIMILAR      string pattern matching
< >     less than, greater than
=   right   equality, assignment
NOT right   logical negation
AND left    logical conjunction
OR  left    logical disjunction
*/
    
    private IndentedWriter out ;

    SqlExprGenerateSQL(IndentedWriter out)
    { this.out = out ; }

    @Override
    public void visit(SqlColumn column)     { out.print(column.asString()) ; }
    
    @Override
    public void visit(SqlConstant constant) { out.print(constant.asSqlString()) ; }
    
    @Override
    public void visit(SqlFunction1 expr)
    {
        out.print(expr.getFuncSymbol()) ;
        out.print("(") ;
        expr.getExpr().visit(this) ;
        out.print(")") ;
    }

    @Override
    public void visit(SqlExpr1 expr)
    {
        printExpr(expr.getExpr()) ;
        out.print(" ") ;
        out.print(expr.getExprSymbol()) ;
    }

    @Override
    public void visit(SqlExpr2 expr)
    {
        printExpr(expr.getLeft()) ;
        out.print(" ") ;
        out.print(expr.getOpSymbol()) ;
        out.print(" ") ;
        printExpr(expr.getRight()) ;
    }

    @Override
    public void visit(S_Like pattern)
    {
        if ( pattern.isCaseInsensitive() )
        {
            out.print("lower(") ;
            pattern.getExpr().visit(this) ;
            out.print(") LIKE ") ;
            out.print(SQLUtils.quoteStr(pattern.getPattern().toLowerCase())) ;
        }
        else
        {
            pattern.getExpr().visit(this) ;
            out.print(" LIKE ") ;
            out.print(SQLUtils.quoteStr(pattern.getPattern())) ;
        }
        return ;
    }
    
    public String RegexOperator = "REGEXP" ; 
    
    @Override
    public void visit(S_Regex regex)
    {
        // TODO Make per-store dependent for syntax and case sensitiveity reasons.
        // including "binary" for MySQL
        regex.getExpr().visit(this) ;
        
        String pattern = regex.getPattern() ;
        String patternLike = RegexUtils.regexToLike(pattern) ;
        if ( patternLike != null )
        {
            out.print(" LIKE ") ;
            out.print(SQLUtils.quoteStr(patternLike)) ;
            return ;
        }
        
        // MySQL :: LIKE // LIKE BINARY
        out.print(" ") ; out.print(RegexOperator) ; out.print(" ") ;
        if ( regex.getFlags() != null && ! regex.getFlags().equals("i") )
            out.print("BINARY ") ;
        out.print(SQLUtils.quoteStr(regex.getPattern())) ;
    }
    
    private void printExpr(SqlExpr expr)
    {
        boolean atomic = expr.isColumn() || expr.isConstant() ;
        if ( ! atomic )
            out.print("( ") ;
        expr.visit(this) ;
        if ( ! atomic )
            out.print(" )") ;
    }
    
    
}
