class TypeFusion::Sampler

def extract_parameters(args, binding)

def extract_parameters(args, binding)
  args.map do |name, kind|
    variable = name.to_s.gsub("*", "").gsub("&", "").to_sym
    type = if binding.local_variables.include?(variable)
             type_for_object(binding.local_variable_get(variable))
           else
             # *, ** or &
             "unused"
           end
    [name, kind, type]
  end
end

def gem_and_version_from(gem_and_version)

def gem_and_version_from(gem_and_version)
  return [] if gem_and_version.nil?
  splits = gem_and_version.split("-")
  if splits.length == 1
    [splits.first, nil]
  elsif splits.length == 2
    splits
  else
    *name, version = splits
    # TODO: there must be a better way to do this
    if ["darwin", "java", "linux", "ucrt", "mingw32"].include?(version)
      amount = (version == "ucrt") ? 3 : 2
      version = [*name.pop(amount), version].join("-")
    end
    gem = name.join("-")
    [gem, version]
  end
end

def gem_path

def gem_path
  "#{Gem.default_path.last}/gems/"
end

def initialize

def initialize
  @samples = []
end

def inspect

def inspect
  "#<TypeFusion::Sampler sample_count=#{@samples.count}>"
end

def reset!

def reset!
  @samples = []
  @trace = nil
end

def sample?(tracepoint_path)

def sample?(tracepoint_path)
  TypeFusion.config.type_sample_call? &&
    TypeFusion.config.type_sample_tracepoint_path?(tracepoint_path) &&
    tracepoint_path.start_with?(gem_path)
end

def to_s

def to_s
  inspect
end

def trace

def trace
  @trace ||= TracePoint.trace(:return) do |tracepoint|
    if sample?(tracepoint.path)
      receiver = begin
        tracepoint.binding.receiver.name
      rescue StandardError
        tracepoint.binding.receiver.class.name
      end
      method_name = tracepoint.method_id
      location = tracepoint.binding.source_location.join(":")
      gem_and_version = location.gsub(gem_path, "").split("/").first
      gem, version = gem_and_version_from(gem_and_version)
      args = tracepoint.parameters.to_h(&:reverse)
      parameters = extract_parameters(args, tracepoint.binding)
      return_value = type_for_object(tracepoint.return_value)
      sample = SampleCall.new(
        gem_name: gem,
        gem_version: version,
        receiver: receiver,
        method_name: method_name,
        location: location,
        type_fusion_version: VERSION,
        parameters: parameters,
        application_name: TypeFusion.config.application_name,
        return_value: return_value,
      )
      samples << sample
      begin
        SampleJob.perform_async(sample)
      rescue StandardError => e
        puts "[TypeFusion] Couldn't enqueue sample: #{e.inspect}"
      end
    end
  end.tap(&:disable)
end

def type_for_object(object)

def type_for_object(object)
  case object
  when Hash
    ["Hash", object.map { |key, value| [key, type_for_object(value)] }]
  when Array
    ["Array", object.map { |value| type_for_object(value) }]
  else
    object.class
  end
end

def with_sampling

def with_sampling
  trace.enable
  yield if block_given?
  trace.disable
end