require 'antwrap'
require 'buildr/packaging/artifact'

module Buildr
  module MavenExtension
    include Extension

    class << self
      # Which version of Maven Ant tasks we're using by default.
      VERSION = '2.1.0'

      def version
        Buildr.settings.build['maven'] || VERSION
      end

      def dependencies
        @dependencies ||= ["org.apache.maven:maven-ant-tasks:jar:#{version}"]
      end

      def ant
        @ant ||= init_ant
        @ant
      end

      def init_ant()
        ant = Buildr::ant("maven-ant")

        #Tasks
        ant.taskdef :name => "dependencies", :classname => "org.apache.maven.artifact.ant.DependenciesTask"
        ant.taskdef :name => "install", :classname => "org.apache.maven.artifact.ant.InstallTask"
        ant.taskdef :name => "deploy", :classname => "org.apache.maven.artifact.ant.DeployTask"
        ant.taskdef :name => "install-provider", :classname => "org.apache.maven.artifact.ant.InstallWagonProviderTask"
        ant.taskdef :name => "mvn", :classname => "org.apache.maven.artifact.ant.Mvn"
        ant.taskdef :name => "writepom", :classname => "org.apache.maven.artifact.ant.WritePomTask"

        # Types
        ant.typedef :name => "localRepository", :classname => "org.apache.maven.artifact.ant.LocalRepository"
        ant.typedef :name => "remoteRepository", :classname => "org.apache.maven.artifact.ant.RemoteRepository"
        ant.typedef :name => "authentication", :classname => "org.apache.maven.artifact.ant.Authentication"
        ant.typedef :name => "proxy", :classname => "org.apache.maven.artifact.ant.Proxy"

        # Tasks that are also types
        ant.taskdef :name => "pom", :classname => "org.apache.maven.artifact.ant.Pom"

        ant
      end
    end

    Java.classpath << lambda { MavenExtension.dependencies }

    class Maven
      def initialize(project)
        @project = project
      end

      def enabled?
        File.exists?(@project.path_to("pom.xml"))
      end

      def id
        pom.getArtifactId
      end

      def group
        pom.getGroupId
      end

      def version
        pom.getVersion
      end

      def classifier
        pom.getClassifier
      end

      def packaging
        pom.getPackaging ? pom.getPackaging.to_sym : :jar
      end

      def pom_spec_hash
        { :group=>group, :id=>id, :type=>packaging, :version=>version }
      end

      def dependencies(*scopes)
        scopes = scopes.flatten.map { |scope| scope.to_sym }
        scopes << :all if scopes.empty?

        deps = []
        scopes.each do |scope|
          @dependencies ||= {}
          @dependencies[scope] ||= load_dependencies scope
          deps += @dependencies[scope]
        end
        deps
      end

      def install
        main, attached = artifacts

        MavenExtension.ant.install( :pomRefId => pom_id, :file => main ) do |ant|
          attached.each do |a|
            ant.attach :file => a, :type => a.type, :classifier => a.classifier
          end
          ant.localRepository :path => Buildr.repositories.local
        end
      end

      def deploy
        upload_to ||= Buildr.repositories.release_to
        upload_to = { :url=>upload_to } unless Hash === upload_to
        raise ArgumentError, 'Don\'t know where to upload, perhaps you forgot to set repositories.release_to' unless upload_to[:url]

        main, attached = artifacts

        MavenExtension.ant.deploy( :pomRefId => pom_id, :file => main ) do |ant|
          attached.each do |a|
            ant.attach :file => a, :type => a.type, :classifier => a.classifier
          end
          ant.remoteRepository :url => upload_to[:url] do |ant|
            credentials = upload_to.reject{ |k, v| ![:username, :password].include? k }
            ant.authentication credentials unless credentials.empty?
          end
        end
      end

      def main_artifact
        artifacts[0]
      end

      def attached_artifacts
        artifacts[1]
      end

      private
      def artifacts
        packages = @project.packages.dup
        spec = pom_spec_hash

        main = packages.find { |p| p.to_spec_hash == spec }
        packages.delete main

        if spec[:classifier] then
          attached = []
        else
          attached = packages.find_all do |p|
            package_spec = p.to_spec_hash
            [:group, :id, :version].all? { |k| package_spec[k] == spec[k] } && package_spec[:classifier]
          end
        end

        return main, attached
      end

      def pom_id
        "#{@project.name}_pom"
      end

      def pom
        @pom ||= load_pom
      end

      def load_pom
        MavenExtension.ant.pom :file => @project.path_to("pom.xml"), :id => pom_id
        MavenExtension.ant.project.getReference(pom_id)
      end

      def load_dependencies(scope)
        fileset_id = "#{@project.name}.#{scope}.dependencies"
        params = { :pomRefId => pom_id, :filesetId => fileset_id }
        params[:scopes] = scope.to_s unless scope == :all

        MavenExtension.ant.dependencies params do |ant|
          Buildr.repositories.remote.each { |url|
            puts "Adding remote repo #{url}"
            ant.remoteRepository :url => url
          }
        end

        fileset = ant.project.getReference(fileset_id)
        scanner = fileset.getDirectoryScanner
        scanner.getIncludedFiles.map{ |f| File.expand_path f, scanner.getBasedir.getAbsolutePath }
      end


    end

    def maven
      @maven ||= Maven.new(self)
    end

    first_time do
      desc 'Resolve project dependencies using Maven'
      Project.local_task('mvn_resolve') { |name| "Resolving #{name}" }

      desc 'Install project packages using Maven'
      Project.local_task('mvn_install') { |name| "Install #{name}" }

      desc 'Deploy project packages using Maven'
      Project.local_task('mvn_deploy') { |name| "Deploying #{name}" }
    end

    before_define do |project|
      if project.maven.enabled?
        class << project
          def id
            maven.id
          end
        end
        project.group = project.maven.group
        project.version = project.maven.version
      end

      resolve = project.recursive_task('mvn_resolve') do
        project.maven.dependencies if project.maven.enabled?
      end
      project.recursive_task('mvn_install')
      project.recursive_task('mvn_deploy')

      project.task('compile' => resolve)
    end

    after_define do |project|
      if project.maven.enabled?
        project.task('mvn_deploy' => project.packages ) { project.maven.deploy }
        project.task('mvn_install' => project.packages ) { project.maven.install }
      end
    end
  end

  class Artifact
    def maven_download
      if (Buildr::MavenExtension.dependencies.include?( to_spec ) || Buildr::Ant.dependencies.include?( to_spec ) )
        buildr_download
      else
        Buildr::MavenExtension.ant.dependencies do |ant|
          spec = to_spec_hash
          params = { :groupId => spec[:group], :artifactId => spec[:id], :version => spec[:version], :type => spec[:type] }
          params[:classifier] = spec[:classifier] if spec.key? :classifier
          ant.dependency params do |ant|
            Buildr.repositories.remote.each { |url| ant.remoteRepository :url => url }
          end
        end
      end
    end

    alias :buildr_download :download
    alias :download :maven_download
  end

  class Project
    include MavenExtension
  end
end

at_exit do
  # The Maven Ant tasks start non-daemon threads. Make sure the JVM is terminated or the build process
  # will never exit.
  Java.java.lang.System.exit( 0 )
end