require 'frank/publish/base'
require 'net/ftptls'

module Frank
  module Publish

    class FTPTLS < Base

      def initialize(options, &block)
        super(options)
        instance_eval(&block) if block_given?

        @port ||= 21
        @remote_path = remote_path.sub(/^\~\//, '').sub(/^\//, '')
      end

      private

      ##
      # Establishes a connection to the remote server
      #
      # Note:
      # Since the FTP port is defined as a constant in the Net::FTP class, and
      # might be required to change by the user, we dynamically remove and
      # re-add the constant with the provided port value
      def connection
        if Net::FTPTLS.const_defined?(:FTP_PORT)
          Net::FTPTLS.send(:remove_const, :FTP_PORT)
        end; Net::FTPTLS.send(:const_set, :FTP_PORT, port)

        Net::FTPTLS.open(hostname, username, password) do |ftp|
          ftp.passive = true
          yield ftp
        end
      end

      ##
      # Transfers the archived file to the specified remote server
      def transfer!
        connection do |ftp|
          directories.each do |dir|
            create_remote_path(File.join(remote_path, dir), ftp)
          end

          files_to_transfer.each do |file|
            ok_message "Uploading #{file}", "    - "
            ftp.put(
              File.join(local_path, file),
              File.join(remote_path, file)
            )
          end
        end
      end

      ##
      # Creates (if they don't exist yet) all the directories on the remote
      # server in order to upload the backup file. Net::FTP does not support
      # paths to directories that don't yet exist when creating new
      # directories. Instead, we split the parts up in to an array (for each
      # '/') and loop through that to create the directories one by one.
      # Net::FTP raises an exception when the directory it's trying to create
      # already exists, so we have rescue it
      def create_remote_path(remote_path, ftp)
        path_parts = []
        remote_path.split('/').each do |path_part|
          path_parts << path_part
          begin
            dir = path_parts.join('/')
            ftp.mkdir(dir) unless dir.empty?
          rescue Net::FTPPermError;
          end
        end
      end


    end


  end
end
