# Transform a nested tructure indicated by dots to a structure as used by the ActiveRecord includes statement
#   - dslams
#   - ppls.distribution_cables
# =>
#   [:dslams, {ppls: :distribution_cables}]
class FlatKeys
  class << self

    def as_nested_structure(ary)
      return ary unless ary.first.is_a?(String)
      nested_keys, flat_keys = ary.partition{|spec| spec['.']}
      result = flat_keys.map(&:to_sym)
      if nested_keys.any?
        obj = {}
        nested_keys.map{ |key| key.split('.').map(&:to_sym) }.each do |parts|
          traverse_nest_structure(parts, obj)
        end
        result -= obj.keys
        result.push obj
      end
      result
    end

    def traverse_nest_structure(parts, obj)
      key = parts.shift
      if parts.size == 1
        case obj[key]
        when nil then obj[key] = parts[0]
        when Array then obj[key] |= [parts[0]]
        when Hash
          raise "Colliding keys for nesting of #{key} -> #{parts[0]}" unless obj[key].has_key?(parts[0])
        else # expect symbol
          obj[key] = Array.wrap(obj[key]).push parts[0] unless obj[key] == parts[0] # no duplicate
        end
      else # parts.size > 2
        case obj[key]
        when nil
          obj[key] = {}
          traverse_nest_structure(parts, obj[key])
        when Array
          raise "Cannot traverse #{key} -> #{parts.join('.')} because existing array value #{obj[key].inspect}"
        when Hash
          traverse_nest_structure(parts, obj[key])
        else # expect symbol
          raise "Cannot add deeper nesting for endpoing with different name #{key} -> #{obj[key]} => #{parts.join('.')}" unless obj[key] = parts[0] # same name, allows deeper nesting
          obj[key] = {obj[key] => nil} # prepare for nesting
          traverse_nest_structure(parts, obj[key])
        end
      end
    end
  end
end
