require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'lib', 'dry_generator'))

class DryModelGenerator < DryGenerator
  
  # Banner: Generator arguments and options.
  BANNER_ARGS = [
      "[_indexes:field,field+field,field,...]"
    ].freeze
  BANNER_OPTIONS = [
      "[--skip-timestamps]",
      "[--skip-migration]"
    ].freeze
    
  # Paths.
  MODELS_PATH =           File.join('app', 'models').freeze
  MIGRATIONS_PATH =       File.join('db', 'migrate').freeze
  
  attr_reader :indexes,
              :references
              
  def initialize(runtime_args, runtime_options = {})
    @options = DEFAULT_OPTIONS.merge(options)
    super(runtime_args, runtime_options.merge(@options))
    
    @attributes ||= []
    args_for_model = []
    
    @args.each do |arg|
      arg_entities = arg.split(':')
      if arg =~ /^#{NON_ATTR_ARG_KEY_PREFIX}/
        if arg =~ /^#{NON_ATTR_ARG_KEY_PREFIX}index/
          arg_indexes = arg_entities[1].split(',').compact.uniq
          @indexes = arg_indexes.collect do |index|
            if index =~ /\+/
              index.split('+').collect { |i| i.downcase.to_sym  }
            else
              index.downcase.to_sym
            end
          end
        end
      else
        @attributes << Rails::Generator::GeneratedAttribute.new(*arg_entities)
        args_for_model << arg
      end
    end
    
    @args = args_for_model
    @references = attributes.select(&:reference?)
  end
  
  def manifest
    record do |m|
      # Check for class naming collisions.
      m.class_collisions class_name, "#{class_name}Test"
      
      # Model.
      m.directory File.join(MODELS_PATH, class_path)
      m.template File.join('models', 'active_record_model.rb'),
        File.join(MODELS_PATH, class_path, "#{file_name}.rb")
        
      # Model Tests.
      unless options[:skip_tests]
        model_tests_path = File.join(TEST_PATHS[test_framework], UNIT_TESTS_PATH[test_framework])
        m.directory File.join(model_tests_path, class_path)
        m.template File.join('models', 'tests', "#{test_framework}", 'unit_test.rb'),
          File.join(model_tests_path, class_path, "#{file_name}_#{TEST_POST_FIX[test_framework]}.rb")
          
        # Fixtures/Factories.
        if options[:fixtures]
          fixtures_path = File.join(TEST_PATHS[test_framework], 'fixtures')
          m.directory File.join(fixtures_path, class_path)
          m.template File.join('models', 'fixture_data', 'active_record_fixtures.yml'),
            File.join(fixtures_path, class_path, "#{table_name}.yml")
        end
        if options[:factory_girl]
          factory_girl_path = File.join(TEST_PATHS[test_framework], 'factories')
          m.directory File.join(factory_girl_path, class_path)
          m.template File.join('models', 'fixture_data', 'factory_girl_factories.rb'),
            File.join(factory_girl_path, class_path, "#{plural_name}.rb")
        end
        if options[:machinist]
          machinist_path = File.join(TEST_PATHS[test_framework], 'blueprints')
          m.directory File.join(machinist_path, class_path)
          m.template File.join('models', 'fixture_data', 'machinist_blueprints.rb'),
            File.join(machinist_path, class_path, "#{plural_name}.rb")
        end
        # NOTE: :object_daddy handled in model
      end
      
      # Migration.
      unless options[:skip_migration]
        m.migration_template File.join('models', 'active_record_migration.rb'), MIGRATIONS_PATH,
          :assigns => {:migration_name => "Create#{class_name.pluralize.gsub(/::/, '')}"},
          :migration_file_name => "create_#{file_path.gsub(/\//, '_').pluralize}"
      end
    end
  end
  
  protected
    
    def add_options!(opt)
      super(opt)
      
      opt.separator ' '
      opt.separator 'Model Options:'
      
      opt.on("--skip-timestamps", "Don't add timestamps to the migration file.") do |v|
        options[:skip_timestamps] = v
      end
      
      opt.on("--skip-migration", "Skip generation of migration file.") do |v|
        options[:skip_migration] = v
      end
      
      opt.on("--skip-tests", "Skip generation of tests.") do |v|
        options[:skip_tests] = v
      end
      
      opt.separator ' '
    end
    
    def banner_args
      [BANNER_ARGS, super].flatten.join(' ')
    end
    
    def banner_options
      [BANNER_OPTIONS, super].flatten.join(' ')
    end
    
    def banner
      [super, banner_args, banner_options].join(' ')
    end
    
end
