require 'shellwords'
class Specinfra::Command::Base
  attr_accessor :types

  class NotImplementedError < Exception; end

  def self.create
    self.new
  end

  def escape(target)
    str = case target
          when Regexp
            target.source
          else
            target.to_s
          end

    Shellwords.shellescape(str)
  end

  def method_missing(meth, *args)
    action, resource_type, subaction = breakdown(meth)
    method =  action
    method += "_#{subaction}" if subaction
    command_object = command_class(resource_type).create
    if command_object.respond_to?(method)
      command_object.send(method, *args)
    else
      raise NotImplementedError.new("#{method} is not implemented in #{command_class(resource_type)}")
    end
  end

  def command_class(resource_type)
    family  = os[:family]
    version = os[:release] ? "V#{os[:release].to_i}" : nil

    common_class = self.class.const_get('Specinfra').const_get('Command')
    base_class   = common_class.const_get('Base')
    os_class     = family.nil? ? base_class : common_class.const_get(family.capitalize)

    if family && version
      begin
        version_class = os_class.const_get(version)
      rescue
        version_class = os_class.const_get('Base')
      end
    elsif family.nil?
      version_class = os_class
    elsif family != 'base' && version.nil?
      version_class = os_class.const_get('Base')
    end

    begin
      command_klass = version_class.const_get(resource_type.to_camel_case)
    rescue
    end

    if command_klass.nil? ||( (command_klass < Specinfra::Command::Base).nil? && (command_klass < Specinfra::Command::Windows::Base).nil? )
      command_klass = base_class.const_get(resource_type.to_camel_case)
    end

    command_klass
  end

  private
  def breakdown(meth)
    types = resource_types.map {|t| t.to_snake_case }.join('|')
    md = meth.to_s.match(/^([^_]+)_(#{types})_?(.+)?$/)
    if md.nil?
      message =  "Could not break down `#{meth}' to appropriate type and method.\n"
      message += "The method name shoud be in the form of `action_type_subaction'."
      raise message
    end
    return md[1], md[2], md[3]
  end

  def resource_types
    if @types.nil?
      @types = []
      Specinfra::Command::Base.subclasses.each do |s|
        @types << s.to_s.split(':')[-1]
      end
      Specinfra::Command::Windows::Base.subclasses.each do |s|
        @types << s.to_s.split(':')[-1]
      end
      @types.uniq!
    end
    @types
  end
end