require 'voodoo/config'

module Voodoo
  # Module for selecting code generators.
  #
  # The code generation API is described in Voodoo::CommonCodeGenerator.
  module CodeGenerator
    # Hash using target architectures as keys.
    # Each entry is a hash mapping output format to generator class.
    @@generators = {}

    module_function

    # Register a code generator.
    # Example:
    #   Voodoo::CodeGenerator.register_generator I386NasmGenerator,
    #                                            :architecture => :i386,
    #                                            :format => :nasm
    def register_generator klass, params
      if params.has_key?(:architecture) && params.has_key?(:format)
        arch = params[:architecture].to_sym
        format = params[:format].to_sym
        format_hash = @@generators[arch] || {}
        format_hash[format] = klass
        @@generators[arch] = format_hash
      else
        raise ArgumentError, "Need to specify :architecture and :format"
      end
    end

    # Get a code generator for the specified parameters
    def get_generator params = {}
      params[:architecture] = Config.default_architecture \
        unless params[:architecture]
      params[:format] = Config.default_format unless params[:format]
      arch = params[:architecture].to_sym
      format = params[:format].to_sym
      format_hash = @@generators[arch]
      klass = format_hash ? format_hash[format] : nil
      if klass
        return klass.new params
      else
        raise NotImplementedError, "No code generator for architecture #{arch} and format #{format}"
      end
    end

    # Tests if a given architecture is supported
    def architecture_supported? arch
      @@generators.has_key? arch.to_sym
    end

    # Get an array of supported architectures
    def architectures
      @@generators.keys
    end

    # Tests if a given format is supported for a given architecture
    def format_supported? arch, format
      architecture_supported?(arch.to_sym) &&
        @@generators[arch.to_sym].has_key?(format.to_sym)
    end

    # Get an array of supported formats for a given architecture
    def formats architecture
      @@generators[architecture.to_sym].keys
    end
  end
end

# Load generators
Dir.glob(File.join(File.dirname(__FILE__), 'generators', '*.rb')).each do |file|
  name = file.sub(/.*(voodoo\/generators\/.*)\.rb/, "\\1")
  require name
end
