require "base64"

module Blather
  class FileTransfer
    # In-Band Bytestreams Transfer helper
    # Takes care of accepting, declining and offering file transfers through the stream
    class Ibb
      def initialize(stream, iq)
        @stream = stream
        @iq = iq
        @seq = 0
      end

      # Accept an incoming file-transfer
      #
      # @param [module] handler the handler for incoming data, see Blather::FileTransfer::SimpleFileReceiver for an example
      # @param [Array] params the params to be passed into the handler
      def accept(handler, *params)
        @io_read, @io_write = IO.pipe
        EM::attach @io_read, handler, *params

        @stream.register_handler :ibb_data, :from => @iq.from, :sid => @iq.sid do |iq|
          if iq.data['seq'] == @seq.to_s
            begin
              @io_write << Base64.decode64(iq.data.content)

              @stream.write iq.reply

              @seq += 1
              @seq = 0 if @seq > 65535
            rescue Errno::EPIPE => e
              @stream.write StanzaError.new(iq, 'not-acceptable', :cancel).to_node
            end
          else
            @stream.write StanzaError.new(iq, 'unexpected-request', :wait).to_node
          end
          true
        end

        @stream.register_handler :ibb_close, :from => @iq.from, :sid => @iq.sid do |iq|
          @stream.write iq.reply
          @stream.clear_handlers :ibb_data, :from => @iq.from, :sid => @iq.sid
          @stream.clear_handlers :ibb_close, :from => @iq.from, :sid => @iq.sid

          @io_write.close
          true
        end

        @stream.clear_handlers :ibb_open, :from => @iq.from
        @stream.clear_handlers :ibb_open, :from => @iq.from, :sid => @iq.sid
        @stream.write @iq.reply
      end

      # Decline an incoming file-transfer
      def decline
        @stream.clear_handlers :ibb_open, :from => @iq.from
        @stream.clear_handlers :ibb_data, :from => @iq.from, :sid => @iq.sid
        @stream.clear_handlers :ibb_close, :from => @iq.from, :sid => @iq.sid
        @stream.write StanzaError.new(@iq, 'not-acceptable', :cancel).to_node
      end

      # Offer a file to somebody, not implemented yet
      def offer
        # TODO: implement
      end
    end
  end
end
