# -------------------------------------------------------------------------- #
# Copyright 2010-2015, C12G Labs S.L.                                        #
#                                                                            #
# 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.                                             #
#--------------------------------------------------------------------------- #

grammar ElasticityGrammar

    rule expression
        space exp:(boolean_exp / logic_cond) space {
            def result(role)
                return exp.result(role)
            end
        }
    end

    rule boolean_exp
        left:logic_cond space op:('&&' / '&') space right:expression {
            def result(role)
                l_val, l_st = left.result(role)
                r_val, r_st = right.result(role)

                st = "#{l_st} #{op.text_value} #{r_st}"
                val = l_val && r_val

                return [val, st]
            end
        }
        /
        left:logic_cond space op:('||' / '|') space right:expression {
            def result(role)
                l_val, l_st = left.result(role)
                r_val, r_st = right.result(role)

                st = "#{l_st} #{op.text_value} #{r_st}"
                val = l_val || r_val

                return [val, st]
            end
        }
    end

    rule logic_cond
#        'true' {
#            def result(role)
#                return true
#            end
#        }
#        /
#        'false' {
#            def result(role)
#                return false
#            end
#        }
#        /
        left:operand space comp_op space right:operand {
            def result(role)
                l_val, l_st = left.result(role)
                r_val, r_st = right.result(role)

                st = "#{l_st} #{comp_op.text_value} #{r_st}"

                if l_val.nil? || r_val.nil?
                    # An attribute was not found, we return false instead
                    # of assuming a value of 0

                    val = false
                else
                    val = comp_op.apply(l_val, r_val)
                end

                return [val, st]
            end
        }
        /
        '!' space expression {
            def result(role)
                e_val, e_st = expression.result(role)

                val = !e_val
                st = "!#{e_st}"

                return [val, st]
            end
        }
        /
        '(' space expression space ')' {
            def result(role)
                e_val, e_st = expression.result(role)

                st = "(#{e_st})"

                return [e_val, st]
            end
        }
    end

    rule comp_op
        ('==' / '=') {
            def apply(a,b)
                a == b
            end
        }
        /
        ('!=' / '<>') {
            def apply(a,b)
                a != b
            end
        }
        /
        '>=' {
            def apply(a,b)
                a >= b
            end
        }
        /
        '>' {
            def apply(a,b)
                a > b
            end
        }
        /
        '<=' {
            def apply(a,b)
                a <= b
            end
        }
        /
        '<' {
            def apply(a,b)
                a < b
            end
        }
    end

    rule operand
        ( number ) {
            def result
                number.result(role)
            end
        }
        /
        ( variable ) {
            def result
                variable.result(role)
            end
        }
    end

    rule number
        '-'? [0-9]+ '.' [0-9]+ {
            def result(role)
                val = text_value.to_f
                st = val.to_s

                return [val, st]
            end
        }
        /
        '-'? [0-9]+ {
            def result(role)
                val = text_value.to_i
                st = val.to_s

                return [val, st]
            end
        }
    end

    rule variable
        ( '"' [a-zA-Z] [0-9a-zA-Z_]* '"'
        / '\'' [a-zA-Z] [0-9a-zA-Z_]* '\''
        / [a-zA-Z] [0-9a-zA-Z_]*
        ) {

            def result(role)
                nodes = role.get_nodes
                total = 0
                n_nodes = 0
                att = text_value.upcase

                nodes.each { |node|
                    if node && node['vm_info']

                        vm_state = node['vm_info']['VM']['STATE']
                        lcm_state = node['vm_info']['VM']['LCM_STATE']

                        # Use values from VMs in RUNNING only

                        if vm_state != '3' || lcm_state != '3'
                            next
                        end

                        if node['vm_info']['VM']['USER_TEMPLATE'][att]
                            total += (node['vm_info']['VM']['USER_TEMPLATE'][att]).to_f
                            n_nodes += 1
                        elsif node['vm_info']['VM'][att]
                            total += (node['vm_info']['VM'][att]).to_f
                            n_nodes += 1
                        elsif node['vm_info']['VM']['TEMPLATE'][att]
                            total += (node['vm_info']['VM']['TEMPLATE'][att]).to_f
                            n_nodes += 1
                        end
                    end
                }

                # The attribute wasn't found for any of the nodes
                if n_nodes == 0
                    val = nil
                    st = "#{att}[--]"
                else
                    val = (total / n_nodes).round(2)
                    st = "#{att}[#{val.to_s}]"
                end

                return [val, st]
            end
        }
    end

    rule space
      ' '*
    end
end