# sql.tcl -- SQL code generator

# Copyright 2002-2004 The Apache Software Foundation

# Licensed 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.

# This class provides a way to abstract to some extent the
# SQL code generation. It's supposed to provide a bridge to
# different implementation in various backends for specific
# functionalities
#
# $Id: sql.tcl 1596885 2014-05-22 14:19:10Z mxmanghi $

package require Itcl

### 
catch { ::itcl::delete class ::DIO::Sql }
###
namespace eval ::Aida {

    proc generator {backend} {
        
    }

    ::itcl::class Sql {

        public variable backend
        public variable what
        public variable table

        constructor { backend } {

        }

        private method where_clause {where_arguments}

        public method build_select_query {table row_d}
        public method quote {field_value}

        protected method field_value {table_name field_name val} {
            return "'[quote $val]'"
        }

        public method build_insert_query {table row_d}
        public method build_update_query {table row_d}

    }
    
    # -- build_insert_query
    #
    #

    ::itcl::body Sql::build_insert_query {table row_d} {

        set vars [dict keys $row_d]
        foreach field $vars {
        
            lappend vals [$this field_value $table $field [dict get $row_d $field]]

        }

        return "INSERT INTO $table ([join $vars {,}]) VALUES ([join $vals {,}])"
    }

    # -- build_update_query
    #
    #

    ::itcl::body Sql::build_update_query {table row_d} {
      
        foreach field [dict keys $row_d] {
            lappend rowfields "$field=[field_value $table $field [dict get $row_d $field]]"
        }

        return "UPDATE $table SET [join $rowfields {,}]"
    }


    # build_where_clause 
    #
    #
    ::itcl::body Sql::where_clause {where_expr} {

        set sql ""
        for {set i 0} {$i < [llength [dict keys $where_expr]]} {incr i} {

            set d [dict get $where_expr $i]

            set col [dict get $d column]
            set op  [dict get $d operator]
            if {$i > 0} {

                append sql " [dict get $d logical]"

            }
            switch $op {

                "eq" {
                    set sqlop "="
                }
                "ne" {
                    set sqlop "!="
                }
                "lt" {
                    set sqlop "<"
                }
                "gt" {
                    set sqlop ">"
                }
                "le" {
                    set sqlop "<="
                }
                "ge" {
                    set sqlop ">="
                }
                "notnull" {

                    append sql " $col IS NOT NULL"
                    continue

                }
                "null" {
                    append sql " $col IS NULL"
                    continue

                }

            }

            set predicate [dict get $d predicate]
            if {[::string first {%} $predicate] != -1} {
                append sql " $col LIKE [$this field_value $table $col [[string range $predicate 1 end]]"
            } else {
                append sql " $col$sqlop[$this field_value $table $col $predicate]"
            }
        }

        return $sql
    }    


    #
    # quote - given a string, return the same string with any single
    #  quote characters preceded by a backslash
    #
    ::itcl::body Sql::quote {field_value} {
        regsub -all {'} $field_value {\'} field_value
        return $field_value
    }

    # build_select_query - build a select query based on given arguments,
    # which can include a table name, a select statement, switches to
    # turn on boolean AND or OR processing, and possibly
    # some key-value pairs that cause the where clause to be
    # generated accordingly

    ::itcl::body Sql::build_select_query {args} {

        set bool    AND
        set first   1
        set req     ""
        set table   $from_table
        set what    "*"

        set parser_st   state0
        set condition_count 0
        set where_expr [dict create]

        # for each argument passed us...
        # (we go by integers because we mess with the index depending on
        # what we find)
        #puts "args: $args"
        for {set i 0} {$i < [llength $args]} {incr i} {

            # fetch the argument we're currently processing
            set elem [lindex $args $i]
            # puts "cycle: $i (elem: $elem, status: $parser_st, first: $first)"
            
            switch $parser_st {
                state0 {

                    switch -- [::string tolower $elem] {

                        # -table and -select don't drive the parser state machine
                        # and whatever they have as arguments on the command
                        # line they're set 

                        "-table" { 
                            # -table -- identify which table the query is about
                            set table [lindex $args [incr i]]
                        }
                        "-select" {
                            # -select - 
                            set what [lindex $args [incr i]]
                        }
                        "-or" - 
                        "-and" {
                            if {$first} {
                                return -code error "$elem can not be the first element of a where clause"
                            } else {
                                incr condition_count
                                dict set where_expr $condition_count logical [string range $elem 1 end] 
                                set parser_st where_op
                            }
                        }    
                        default {
                            
                            if {[::string index $elem 0] == "-"} {
                                if {!$first} { 
                                    incr condition_count 
                                }
                                dict set where_expr $condition_count column [string range $elem 1 end]
                                set first 0
                                set parser_st where_op
                            } else {

                                return -code error "Error: expected -<column_name>"
                            }

                        }

                    }

                }

                where_op {

                    switch -- [string tolower $elem] {

                        "-lt" -
                        "-gt" -
                        "-ne" -
                        "-eq" {

                            dict set where_expr $condition_count operator [string range $elem 1 end]
                            set parser_st cond_predicate

                        }
                         
                        "-null" -
                        "-notnull" {

                            dict set where_expr $condition_count operator [string range $elem 1 end]
                            set parser_st state0

                        }

                        default {
                            if {[::string index $elem 0] == "-"} {
                                dict set where_expr $condition_count column [string range $elem 1 end]
                            } else {
                                dict set where_expr $condition_count operator "eq"
                                dict set where_expr $condition_count predicate $elem 
                                set parser_st state0
                            }
                        }

                    }
                }

                cond_predicate {
                    
                    switch -- [string tolower $elem] {

                        "-expr" {
                            dict set where_expr $condition_count predicate [lindex $args [incr i]] 
                        }
                        default  {

                            # convert any asterisks to percent signs in the
                            # value field
                            regsub -all {\*} $elem {%} elem

                            dict set where_expr $condition_count predicate $elem

                        }

                    }
                    set parser_st state0
                }
                default {
                    return -code error "invalid parser status"
                }
            }
        }

        set sql "SELECT $what from $table WHERE[$this where_clause $where_expr]"

        return $sql
    }




}




