require 'delegate'

##
# DNSSD::TextRecord is a Hash delegate that can encode its contents for DNSSD.

class DNSSD::TextRecord < DelegateClass(Hash)
  def self.decode text_record
    record = {}

    tr = text_record.unpack 'C*'

    until tr.empty? do
      size  = tr.shift

      next if size.zero?

      raise ArgumentError, 'ran out of data in text record' if tr.length < size

      entry = tr.shift(size).pack('C*')

      raise ArgumentError, 'key not found' unless entry =~ /^[^=]/

      key, value = entry.split '=', 2

      next unless key

      record[key] = value
    end

    new record
  end

  ##
  # Creates a new TextRecord decoding an encoded +text_record+ if given or
  # from a given Hash.
  #
  #   DNSSD::TextRecord.new "\003k=v"
  #
  # or
  #
  #   DNSSD::TextRecord.new 'k' => 'v'

  def initialize(text_record = nil)
    super case text_record
          when Hash then
            text_record.dup
          when String then
            decode(text_record.dup)
          else
            Hash.new
          end
  end

  ##
  # Decodes +text_record+ and returns a Hash

  def decode(text_record)
    self.class.decode text_record
  end

  ##
  # Encodes this TextRecord.  A key value pair must be less than 255 bytes in
  # length.  Keys longer than 14 bytes may not be compatible with all
  # clients.

  def encode
    sort.map do |key, value|
      key = key.to_s

      raise DNSSD::Error, "empty key" if key.empty?
      raise DNSSD::Error, "key '#{key}' contains =" if key =~ /=/

      record = value ? [key, value.to_s].join('=') : key

      raise DNSSD::Error, "key value pair at '#{key}' too large to encode" if
        record.length > 255

      "#{record.length.chr}#{record}"
    end.join ''
  end

end

