# frozen_string_literal: true

module TTY
  class Command
    # Retains the first N bytes and the last N bytes from written content
    #
    # @api private
    class Truncator
      # Default maximum byte size for prefix & suffix
      DEFAULT_SIZE = 32 << 10

      # Create a Truncator
      #
      # @param [Hash] options
      # @option options [Number] max_size
      #
      # @api public
      def initialize(options = {})
        @max_size = options.fetch(:max_size) { DEFAULT_SIZE }
        @prefix   = ""
        @suffix   = ""
        @skipped  = 0
      end

      # Write content
      #
      # @param [String] content
      #   the content to write
      #
      # @return [nil]
      #
      # @api public
      def write(content)
        content = content.to_s.dup

        content, @prefix = append(content, @prefix)

        if (over = (content.bytesize - @max_size)) > 0
          content = content.byteslice(over..-1)
          @skipped += over
        end

        content, @suffix = append(content, @suffix)

        # suffix is full but we still have content to write
        while content.bytesize > 0
          content = copy(content, @suffix)
        end
      end
      alias << write

      # Truncated representation of the content
      #
      # @return [String]
      #
      # @api public
      def read
        return @prefix if @suffix.empty?

        if @skipped.zero?
          return @prefix << @suffix
        end

        @prefix + "\n... omitting #{@skipped} bytes ...\n" + @suffix
      end
      alias to_s read

      private

      # Copy minimum bytes from source to destination
      #
      # @return [String]
      #   the remaining content
      #
      # @api private
      def copy(value, dest)
        bytes = value.bytesize
        n = bytes < dest.bytesize ? bytes : dest.bytesize

        head, tail = dest.byteslice(0...n), dest.byteslice(n..-1)
        dest.replace("#{tail}#{value[0...n]}")
        @skipped += head.bytesize
        value.byteslice(n..-1)
      end

      # Append value to destination
      #
      # @param [String] value
      #
      # @param [String] dst
      #
      # @api private
      def append(value, dst)
        remain    = @max_size - dst.bytesize
        remaining = ""
        if remain > 0
          value_bytes = value.to_s.bytesize
          offset = value_bytes < remain ? value_bytes : remain
          remaining = value.byteslice(0...offset)
          value = value.byteslice(offset..-1)
        end
        [value, dst + remaining]
      end
    end # Truncator
  end # Command
end # TTY
