module Jabber
  module Bytestreams
    ##
    # SOCKS5Bytestreams implementation for the initiator side
    class SOCKS5BytestreamsInitiator < SOCKS5Bytestreams
      attr_reader :streamhosts

      def initialize(stream, session_id, initiator_jid, target_jid)
        super
        @streamhosts = []
      end

      ##
      # Add a streamhost which will be offered to the target
      #
      # streamhost:: can be:
      # * [StreamHost] if already got all information (host/port)
      # * [SOCKS5BytestreamsServer] if this is the local streamhost
      # * [String] or [JID] if information should be automatically resolved by SOCKS5Bytestreams::query_streamhost
      def add_streamhost(streamhost)
        if streamhost.kind_of?(StreamHost)
          @streamhosts << streamhost
        elsif streamhost.kind_of?(SOCKS5BytestreamsServer)
          streamhost.each_streamhost(@initiator_jid) { |sh|
            @streamhosts << sh
          }
        elsif streamhost.kind_of?(String) or streamhost.kind_of?(JID)
          @streamhosts << SOCKS5Bytestreams::query_streamhost(@stream, streamhost, @initiator_jid)
        else
          raise "Unknwon streamhost type: #{streamhost.class}"
        end
      end

      ##
      # Send the configured streamhosts to the target,
      # wait for an answer and
      # connect to the host the target chose.
      def open
        iq1 = Iq::new(:set, @target_jid)
        iq1.from = @initiator_jid
        bs = iq1.add IqQueryBytestreams.new(@session_id)
        @streamhosts.each { |se|
          bs.add(se)
        }

        peer_used = nil
        @stream.send_with_id(iq1) { |response|
          if response.type == :result and response.query.kind_of?(IqQueryBytestreams)
            peer_used = response.query.streamhost_used
            raise "No streamhost-used" unless peer_used
            raise "Invalid streamhost-used" unless peer_used.jid
          end
          true
        }

        @streamhost_used = nil
        @streamhosts.each { |sh|
          if peer_used.jid == sh.jid
            @streamhost_used = sh
            break
          end
        }
        if @streamhost_used.jid == @initiator_jid
          # This is our own JID, so the target chose SOCKS5BytestreamsServer
          @socks = @streamhost_used.server.peer_sock(stream_address)
          raise "Target didn't connect" unless @socks
          @streamhost_cbs.process(@streamhost_used, :success, nil)
        else
          begin
            @socks = connect_socks(@streamhost_used)
          rescue Exception => e
            Jabber::debuglog("SOCKS5 Bytestreams: #{e.class}: #{e}\n#{e.backtrace.join("\n")}")
            @streamhost_cbs.process(@streamhost_used, :failure, e)
            raise e
          end
          iq2 = Iq::new(:set, @streamhost_used.jid)
          iq2.add(IqQueryBytestreams.new(@session_id)).activate = @target_jid.to_s
          @stream.send_with_id(iq2) { |reply|
            reply.type == :result
          }
        end
      end
    end
  end
end
