require 'open-uri'

require 'build-tool/vcs/base'
require 'fileutils'


module BuildTool; module VCS

    class ArchiveConfiguration < BaseConfiguration

        def name
            "archive"
        end

        def initialize
            super
            @repository = nil
            @remote_path = nil
        end

        def vcs( mod )
            raise StandardError if @module and ! mod.equal?( @module )
            @module = mod
            Archive.new( self )
        end

        def copy_configuration( other )
            super
        end

        attr_writer :repository
        def repository
            if @repository.nil?
                 raise ConfigurationError, "No repository configured for module #{mod.name}."
            end
            @repository
        end

        attr_writer :remote_path
        def remote_path
            if @remote_path.nil?
                return @module.name
            end
            @remote_path
        end

    end # class ArchiveConfiguration

    class Archive < Base

        class ArchiveError < BuildTool::Error; end
        class FileNotFoundError < BuildTool::Error; end

        #
        ### Attributes
        #
        def name
            "archive"
        end

        def fetching_supported?
            true
        end

        def patches_supported?
            true
        end

        def apply_patches_after_rebase?
            true
        end

        def archive_local_path
            "#{File.dirname( local_path )}/#{archive_name}"
        end

        def archive_name
            File.basename( archive_url )
        end

        def archive_url
            "#{config.repository.url}/#{config.remote_path}"
        end

        #
        ### METHODS
        #

        def checkedout?
            File.exist? archive_local_path
        end

        def apply_patches( patches )
            patches.each do |patch|
                full_path = self.recipe.find_first_config_file( "custom/#{config.module.name}/patches/#{patch}.patch" )
                if full_path.nil?
                    raise FileNotFoundError, "Patch '#{patch}' not found from module #{config.module.name}."
                end
                logger.info "Applying patch #{patch}"
                self.class.execute( "patch -p1 -i #{full_path}", local_path )
            end

        end

        def clone
            fetch()
            rebase()
            0
        end

        def fetch()
            # Check if the archive is already downloaded
            if File.exist? archive_local_path
                logger.trace "Archive already fetched. Skipping."
                return 0
            end

            # If the archive doesn't exist we assume the unpacked archive
            # should not exist too.
            if local_path_exist?
                logger.trace "We fetch a new archive. Remove the old sources."
                FileUtils.rm_rf local_path if !$noop
            end

            # Create the directories parent dir.
            FileUtils.mkdir_p( File.dirname( local_path ) ) if !$noop

            # Download the archive
            logger.trace "get #{archive_name} from #{archive_url} to #{archive_local_path}"
            if !$noop
                target = open( archive_local_path, "wb" )
                open( archive_url ) { |file|
                    while c = file.getc
                        target.putc c
                    end
                }
                target.close()
            end
            return 0
        end

        def guess_top_level_directory
            return "<topdir>" if $noop
            topdir = nil
            if archive_name =~ /(\.tgz|\.tar\.gz)$/
                if self.class.execute( "gunzip -c #{archive_local_path} | tar tf -" ) { |line| 
                    rc = /^([^\/]*)\//.match( line )
                    if topdir and topdir != rc[1]
                        raise StandardError, "Unable to determine toplevel directory for archive #{archive_name}!"
                    end
                    topdir = rc[1]
                } != 0 
                    # No topdir
                    return nil
                end
            else
                raise NotImplementedError, "No idea how to unpack the archive '#{archive_name}'."
            end
            return topdir
        end

        def rebase

            # Check if the archive is already downloaded
            if !File.exist? archive_local_path
                logger.debug "Archive not fetched. Skipping."
                return 0
            end

            if local_path_exist?
                return 0
            end

            # Create the directory we want to use to checkout
            FileUtils.mkdir_p local_path if !$noop

            if archive_name =~ /(\.tgz|\.tar\.gz)$/
                if guess_top_level_directory()
                    cmd = "gunzip -c #{archive_local_path} | tar --strip-components=1 --extract --file=- --directory=#{local_path}"
                else
                    cmd = "gunzip -c #{archive_local_path} | tar --strip-components=0 --extract --file=- --directory=#{local_path}"
                end
                if self.class.execute( cmd ) != 0
                    raise StandardError, "Failed to unpack the archive: $?"
                end
            else
                raise NotImplementedError, "No idea how to unpack the archive"
            end

            0
        end

        def prepare_for_access
            # If our server has an associated ssh-key, add it to the ssh-agent.
            if config.repository.sshkey
                key = config.repository.sshkey
                logger.info ""
                logger.info "#### Adding required ssh-key #{key.file} to ssh-agent."
                MJ::Tools::SSH::add_key key.file
            end
            true
        end

    end # class Archive

end; end