require 'jbundler/configurator'
require 'jbundler/classpath_file'
require 'jbundler/vendor'
require 'jbundler/gemfile_lock'
require 'jbundler/show'
require 'maven/tools/gemspec_dependencies'
require 'maven/tools/jarfile'
require 'maven/ruby/maven'
require 'fileutils'
require 'jar_installer'

module JBundler
  class LockDown

    def initialize( config )
      @config = config
      @configurator = Configurator.new( config )
    end
    
    def vendor
      @vendor ||= JBundler::Vendor.new( @config.vendor_dir )
    end

    def update( debug = false, verbose = false )
      if vendor.vendored?
        raise 'Cannot update vendored jars'
      end

      FileUtils.rm_f( @config.jarfile_lock )
      
      lock_down( false, debug, verbose )
    end
    
    def lock_down( needs_vendor = false, debug = false, verbose = false )
      jarfile = Maven::Tools::Jarfile.new( @config.jarfile )
      classpath = JBundler::ClasspathFile.new( @config.classpath_file )
      if jarfile.exists_lock? && classpath.exists?
        needs_update = false
      else
        needs_update = needs_update?( jarfile, classpath )
      end
      if ( ! needs_update && ! needs_vendor ) || vendor.vendored?

        puts 'Jar dependencies are up to date !'

        if needs_update?( jarfile, classpath )
          f = classpath.file.sub(/#{Dir.pwd}#{File::SEPARATOR}/, '' )
          "the #{f} is stale, i.e. Gemfile or Jarfile is newer. `jbundle update` will update it"
        end
      else

        puts '...'
       
        deps = install_dependencies( debug, verbose, jarfile.exists_lock? )

        update_files( classpath, collect_jars( deps ) ) if needs_update

        vendor_it( vendor, deps ) if needs_vendor

      end
    end

    private

    def needs_update?( jarfile, classpath )
      gemfile_lock = JBundler::GemfileLock.new( jarfile, 
                                                @config.gemfile_lock )
      classpath.needs_update?( jarfile, gemfile_lock )
    end

    def vendor_it( vendor, deps )
      puts "vendor directory: #{@config.vendor_dir}"
      vendor.vendor_dependencies( deps )
      puts
    end

    def collect_jars( deps )
      jars = {}
      deps.each do |d|
        case d.scope
        when :provided
          ( jars[ :jruby ] ||= [] ) << d
        when :test
          ( jars[ :test ] ||= [] ) << d
        else
          ( jars[ :runtime ] ||= [] ) << d
        end
      end
      jars
    end

    def update_files( classpath_file, jars )
      if jars.values.flatten.size == 0
        FileUtils.rm_f @config.jarfile_lock
      else
        lock = Maven::Tools::DSL::JarfileLock.new( @config.jarfile )
        lock.replace( jars )
        lock.dump
      end
      classpath_file.generate( (jars[ :runtime ] || []).collect { |j| j.file },
                               (jars[ :test ] || []).collect { |j| j.file },
                               (jars[ :jruby ] || []).collect { |j| j.file },
                               @config.local_repository )
    end

    def install_dependencies( debug, verbose, exclude_transitive = false )
      deps_file = File.join( File.expand_path( @config.work_dir ), 
                               'dependencies.txt' )
 
      exec_maven( debug, verbose, deps_file, exclude_transitive )

      result = []
      File.read( deps_file ).each_line do |line|
        dep = Jars::JarInstaller::Dependency.new( line )
        result << dep if dep
      end
      result
    ensure
      FileUtils.rm_f( deps_file ) if deps_file
    end

    def exec_maven( debug, verbose, output, exclude_transitive = false )
      m = Maven::Ruby::Maven.new
      m.options[ '-f' ] = File.join( File.dirname( __FILE__ ), 
                                     'dependency_pom.rb' )
      m.property( 'verbose', debug || verbose )
      m.options[ '-q' ] = nil if !debug and !verbose
      m.options[ '-e' ] = nil if !debug and verbose
      m.options[ '-X' ] = nil if debug
      m.options[ '-DexcludeTransitive' ] = exclude_transitive
      m.verbose = debug
      m.property( 'jbundler.outputFile', output )

      @configurator.configure( m )

      attach_jar_coordinates( m )

      m.exec( 'dependency:list' )
    end

    private

    def attach_jar_coordinates( maven )
      load_path = $LOAD_PATH.dup
      require 'bundler/setup'
      done = []
      index = 0
      Gem.loaded_specs.each do |name, spec|
        # TODO get rid of this somehow
        deps = Maven::Tools::GemspecDependencies.new( spec )
        deps.java_dependency_artifacts.each do |a|
          if not done.include?( a.key ) and (a.scope.nil? or a.scope == :compile or a.scope == :runtime)
            maven.property( "jbundler.jars.#{index}", a.to_s )
            index += 1
            done << a.key
          end
        end
      end
      if index > 0
        maven.property( "jbundler.jars.size", index )
      end
    ensure
      $LOAD_PATH.replace( load_path )
    end
  end
end
