# encoding: utf-8

require 'thread'

module TuneMyGc
  class Snapshotter
    UNITS_OF_WORK = /PROCESSING_STARTED|PROCESSING_ENDED/
    TERMINATED = /TERMINATED/
    MAX_SAMPLES = (ENV['RUBY_GC_MAX_SAMPLES'] ? Integer(ENV['RUBY_GC_MAX_SAMPLES']) : 2000)

    attr_reader :buffer
    attr_accessor :unit_of_work
    attr_reader :stat_keys

    def initialize(buf = Queue.new)
      @buffer = buf
      @unit_of_work = false
      @stat_keys = GC.stat.keys
    end

    def take(stage, meta = nil)
      _buffer([TuneMyGc.walltime, TuneMyGc.peak_rss, TuneMyGc.current_rss, stage, GC.stat.values_at(*stat_keys), GC.latest_gc_info, meta, thread_id])
    end

    # low level interface, for tests and GC callback
    def take_raw(snapshot)
      _buffer(snapshot)
    end

    def clear
      @buffer.clear
    end

    def size
      @buffer.size
    end

    def deq
      @buffer.deq
    end

    def empty?
      @buffer.empty?
    end

    private
    def thread_id
      if Thread.current != Thread.main
        Thread.current.object_id
      end
    end

    def _buffer(snapshot)
      if snapshot[3] =~ TERMINATED || size < MAX_SAMPLES
        unless self.unit_of_work
          self.unit_of_work = true if snapshot[3] =~ UNITS_OF_WORK
        end
        @buffer << snapshot
      else
        TuneMyGc.log "Discarding snapshot #{snapshot.inspect} (max samples threshold of #{MAX_SAMPLES} reached)"
      end
    end
  end
end