# Copyright 2017 Google LLC
#
# 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
#
#     https://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.


require "google/cloud/debugger/breakpoint"

module Google
  module Cloud
    module Debugger
      ##
      # # Logpoint
      #
      # A kind of {Google::Cloud::Debugger::Breakpoint} that can be evaluated
      # to generate a formatted log string, which later can be submitted to
      # Stackdriver Logging service
      #
      class Logpoint < Breakpoint
        ##
        # Evaluate the breakpoint unless it's already marked as completed.
        # Store evaluted expressions and stack frame variables in
        # `@evaluated_expressions` and `@evaluated_log_message`.
        #
        # @param [Array<Binding>] call_stack_bindings An array of Ruby Binding
        #   objects, from the call stack that leads to the triggering of the
        #   breakpoints.
        #
        # @return [Boolean] True if evaluated successfully; false otherwise.
        #
        def evaluate call_stack_bindings
          synchronize do
            binding = call_stack_bindings[0]

            return false if complete? || !check_condition(binding)

            begin
              evaluate_log_message binding
            rescue StandardError
              return false
            end
          end

          true
        end

        ##
        # @private Evaluate the expressions and log message. Store the result
        # in @evaluated_log_message
        def evaluate_log_message binding
          evaluated_expressions = expressions.map do |expression|
            Evaluator.readonly_eval_expression binding, expression
          end

          @evaluated_log_message =
            format_message log_message_format, evaluated_expressions
        end

        ##
        # @private Format log message by interpolate expressions.
        #
        # @example
        #   log_point = Google::Cloud::Debugger::Logpoint.new
        #   log_point.format_message(
        #     "Hello $0", ["World"]) #=> "Hello \"World\""
        #
        # @param [String] message_format The message with with
        #   expression placeholders such as `$0`, `$1`, etc.
        # @param [Array<Google::Cloud::Debugger::Breakpoint::Variable>]
        #   expressions An array of evaluated expression variables to be
        #   placed into message_format's placeholders. The variables need
        #   to have type equal String.
        #
        # @return [String] The formatted message string
        #
        def format_message message_format, expressions
          # Substitute placeholders with expressions
          message = message_format.gsub(/(?<!\$)\$\d+/) do |placeholder|
            index = placeholder.match(/\$(\d+)/)[1].to_i
            index < expressions.size ? expressions[index].inspect : ""
          end

          # Unescape "$" characters
          message.gsub(/\$\$/, "$")
        end
      end
    end
  end
end
