module Facter
  module Util
    # A util module for facter containing helper methods
    module Values
      module_function

      class DeepFreezeError < StandardError; end

      # Duplicate and deeply freeze a given data structure
      #
      # @param value [Object] The structure to freeze
      # @return [void]
      def deep_freeze(value)
        case value
        when Numeric, Symbol, TrueClass, FalseClass, NilClass
          # These are immutable values, we can safely ignore them
          value
        when String
          value.dup.freeze
        when Array
          value.map do |entry|
            deep_freeze(entry)
          end.freeze
        when Hash
          value.inject({}) do |hash, (key, value)|
            hash[deep_freeze(key)] = deep_freeze(value)
            hash
          end.freeze
        else
          raise DeepFreezeError, "Cannot deep freeze #{value}:#{value.class}"
        end
      end

      class DeepMergeError < StandardError; end

      # Perform a deep merge of two nested data structures.
      #
      # @param left [Object]
      # @param right [Object]
      # @param path [Array<String>] The traversal path followed when merging nested hashes
      #
      # @return [Object] The merged data structure.
      def deep_merge(left, right, path = [], &block)
        ret = nil

        if left.is_a? Hash and right.is_a? Hash
          ret = left.merge(right) do |key, left_val, right_val|
            path.push(key)
            merged = deep_merge(left_val, right_val, path)
            path.pop
            merged
          end
        elsif left.is_a? Array and right.is_a? Array
          ret = left.dup.concat(right)
        elsif right.nil?
          ret = left
        elsif left.nil?
          ret = right
        elsif left.nil? and right.nil?
          ret = nil
        else
          msg = "Cannot merge #{left.inspect}:#{left.class} and #{right.inspect}:#{right.class}"
          if not path.empty?
            msg << " at root"
            msg << path.map { |part| "[#{part.inspect}]" }.join
          end
          raise DeepMergeError, msg
        end

        ret
      end

      def convert(value)
        value = value.to_s if value.is_a?(Symbol)
        value = value.downcase if value.is_a?(String)
        value
      end
    end
  end
end